Add capture functionality to OMAP pwm driver
Subsystems (2)
cover Add capture functionality to OMAP pwm driver
Folowing patchset is based on Keerthy's "v6 omap: dmtimer: Move driver out of plat-omap" minus last patch which is here corrected.
Keerthy, feel free to pick cleanup/bugfix patches into your serie if you find it usefull.
Tony, I wanted to do something easy to understand as a base for event capture interrupt, but either I do not understand hardware, or it indeed cannot do pulse capture.
Please see comments in the last patch in the serie.
Ladislav Michl (5):
clocksource: timer-dm: Make unexported functions static
clocksource: timer-dm: Check prescaler value
pwm: pwm-omap-dmtimer: Fix frequency when using prescaler
clocksource: timer-dm: Add event capture
pwm: pwm-omap-dmtimer: Add capture functionality
drivers/clocksource/timer-dm.c | 269 +++++++++++++++++------------ drivers/pwm/pwm-omap-dmtimer.c | 200 +++++++++++++++++---- include/clocksource/dmtimer.h | 24 --- include/linux/platform_data/dmtimer-omap.h | 8 + 4 files changed, 334 insertions(+), 167 deletions(-)
Signature · 1 line
2.15.1
PATCH 1/5 clocksource: timer-dm: Make unexported functions static
As dmtimer no longer exports functions, make those previously exported static.
drivers/clocksource/timer-dm.c +111 -111
@@ -163,6 +163,92 @@ static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer)return ret; } +static int omap_dm_timer_set_source (struct omap_dm_timer *timer,int source) +{ +int ret; +char *parent_name =NULL ; +struct clk *parent; +struct dmtimer_platform_data *pdata; + +if (unlikely (!timer)) +return -EINVAL ; + + pdata = timer->pdev->dev.platform_data; + +if (source <0 || source >=3 ) +return -EINVAL ; + +/* +* FIXME: Used for OMAP1 devices only because they do not currently +* use the clock framework to set the parent clock. To be removed +* once OMAP1 migrated to using clock framework for dmtimers +*/ +if (pdata && pdata->set_timer_src) +return pdata->set_timer_src (timer->pdev, source); + +if (IS_ERR (timer->fclk)) +return -EINVAL ; + +#if defined(CONFIG_COMMON_CLK ) +/* Check if the clock has configurable parents */ +if (clk_hw_get_num_parents (__clk_get_hw (timer->fclk)) <2 ) +return 0 ; +#endif + +switch (source) { +case OMAP_TIMER_SRC_SYS_CLK : + parent_name ="timer_sys_ck" ; +break ; + +case OMAP_TIMER_SRC_32_KHZ : + parent_name ="timer_32k_ck" ; +break ; + +case OMAP_TIMER_SRC_EXT_CLK : + parent_name ="timer_ext_ck" ; +break ; + } + + parent =clk_get (&timer->pdev->dev, parent_name); +if (IS_ERR (parent)) { +pr_err ("%s: %s not found\n" , __func__, parent_name); +return -EINVAL ; + } + + ret =clk_set_parent (timer->fclk, parent); +if (ret <0 ) +pr_err ("%s: failed to set %s as parent\n" , __func__, + parent_name); + +clk_put (parent); + +return ret; +} + +static void omap_dm_timer_enable (struct omap_dm_timer *timer) +{ +int c; + +pm_runtime_get_sync (&timer->pdev->dev); + +if (!(timer->capability &OMAP_TIMER_ALWON )) { +if (timer->get_context_loss_count) { + c = timer->get_context_loss_count (&timer->pdev->dev); +if (c != timer->ctx_loss_count) { +omap_timer_restore_context (timer); + timer->ctx_loss_count = c; + } + }else { +omap_timer_restore_context (timer); + } + } +} + +static void omap_dm_timer_disable (struct omap_dm_timer *timer) +{ +pm_runtime_put_sync (&timer->pdev->dev); +} +static int omap_dm_timer_prepare (struct omap_dm_timer *timer) {int rc;
@@ -298,16 +384,16 @@ static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data)return timer; } -struct omap_dm_timer *omap_dm_timer_request (void ) +static struct omap_dm_timer *omap_dm_timer_request (void ) {return _omap_dm_timer_request (REQUEST_ANY ,NULL ); } -struct omap_dm_timer *omap_dm_timer_request_specific (int id) +static struct omap_dm_timer *omap_dm_timer_request_specific (int id) {/* Requesting timer by ID is not supported when device tree is used */ if (of_have_populated_dt ()) { -pr_warn ("%s: Please use omap_dm_timer_request_by_cap/node()\n" , +pr_warn ("%s: Please use omap_dm_timer_request_by_node()\n" , __func__);return NULL ; }
@@ -336,7 +422,7 @@ struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)* Request a timer based upon a device node pointer. Returns pointer to * timer handle on success and a NULL pointer on failure. */ -struct omap_dm_timer *omap_dm_timer_request_by_node (struct device_node *np) +static struct omap_dm_timer *omap_dm_timer_request_by_node (struct device_node *np) {if (!np)return NULL ;
@@ -344,7 +430,7 @@ struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np)return _omap_dm_timer_request (REQUEST_BY_NODE , np); } -int omap_dm_timer_free (struct omap_dm_timer *timer) +static int omap_dm_timer_free (struct omap_dm_timer *timer) {if (unlikely (!timer))return -EINVAL ;
@@ -356,30 +442,6 @@ int omap_dm_timer_free(struct omap_dm_timer *timer)return 0 ; } -void omap_dm_timer_enable (struct omap_dm_timer *timer) -{ -int c; - -pm_runtime_get_sync (&timer->pdev->dev); - -if (!(timer->capability &OMAP_TIMER_ALWON )) { -if (timer->get_context_loss_count) { - c = timer->get_context_loss_count (&timer->pdev->dev); -if (c != timer->ctx_loss_count) { -omap_timer_restore_context (timer); - timer->ctx_loss_count = c; - } - }else { -omap_timer_restore_context (timer); - } - } -} - -void omap_dm_timer_disable (struct omap_dm_timer *timer) -{ -pm_runtime_put_sync (&timer->pdev->dev); -} -int omap_dm_timer_get_irq (struct omap_dm_timer *timer) {if (timer)
@@ -424,7 +486,7 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)#else -struct clk *omap_dm_timer_get_fclk (struct omap_dm_timer *timer) +static struct clk *omap_dm_timer_get_fclk (struct omap_dm_timer *timer) {if (timer && !IS_ERR (timer->fclk))return timer->fclk;
@@ -451,7 +513,7 @@ int omap_dm_timer_trigger(struct omap_dm_timer *timer)return 0 ; } -int omap_dm_timer_start (struct omap_dm_timer *timer) +static int omap_dm_timer_start (struct omap_dm_timer *timer) {u32 l;
@@ -471,7 +533,7 @@ int omap_dm_timer_start(struct omap_dm_timer *timer)return 0 ; } -int omap_dm_timer_stop (struct omap_dm_timer *timer) +static int omap_dm_timer_stop (struct omap_dm_timer *timer) {unsigned long rate =0 ;
@@ -494,70 +556,8 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer)return 0 ; } -int omap_dm_timer_set_source (struct omap_dm_timer *timer,int source) -{ -int ret; -char *parent_name =NULL ; -struct clk *parent; -struct dmtimer_platform_data *pdata; - -if (unlikely (!timer)) -return -EINVAL ; - - pdata = timer->pdev->dev.platform_data; - -if (source <0 || source >=3 ) -return -EINVAL ; - -/* -* FIXME: Used for OMAP1 devices only because they do not currently -* use the clock framework to set the parent clock. To be removed -* once OMAP1 migrated to using clock framework for dmtimers -*/ -if (pdata && pdata->set_timer_src) -return pdata->set_timer_src (timer->pdev, source); - -if (IS_ERR (timer->fclk)) -return -EINVAL ; - -#if defined(CONFIG_COMMON_CLK ) -/* Check if the clock has configurable parents */ -if (clk_hw_get_num_parents (__clk_get_hw (timer->fclk)) <2 ) -return 0 ; -#endif - -switch (source) { -case OMAP_TIMER_SRC_SYS_CLK : - parent_name ="timer_sys_ck" ; -break ; - -case OMAP_TIMER_SRC_32_KHZ : - parent_name ="timer_32k_ck" ; -break ; - -case OMAP_TIMER_SRC_EXT_CLK : - parent_name ="timer_ext_ck" ; -break ; - } - - parent =clk_get (&timer->pdev->dev, parent_name); -if (IS_ERR (parent)) { -pr_err ("%s: %s not found\n" , __func__, parent_name); -return -EINVAL ; - } - - ret =clk_set_parent (timer->fclk, parent); -if (ret <0 ) -pr_err ("%s: failed to set %s as parent\n" , __func__, - parent_name); - -clk_put (parent); - -return ret; -} - -int omap_dm_timer_set_load (struct omap_dm_timer *timer,int autoreload, -unsigned int load) +static int omap_dm_timer_set_load (struct omap_dm_timer *timer,int autoreload, +unsigned int load) {u32 l;
@@ -582,8 +582,8 @@ int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, }/* Optimized set_load which removes costly spin wait in timer_start */ -int omap_dm_timer_set_load_start (struct omap_dm_timer *timer,int autoreload, -unsigned int load) +static int omap_dm_timer_set_load_start (struct omap_dm_timer *timer, +int autoreload,unsigned int load) {u32 l;
@@ -609,9 +609,8 @@ int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, timer->context.tcrr = load;return 0 ; } - -int omap_dm_timer_set_match (struct omap_dm_timer *timer,int enable, -unsigned int match) +static int omap_dm_timer_set_match (struct omap_dm_timer *timer,int enable, +unsigned int match) {u32 l;
@@ -634,8 +633,8 @@ int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,return 0 ; } -int omap_dm_timer_set_pwm (struct omap_dm_timer *timer,int def_on, -int toggle,int trigger) +static int omap_dm_timer_set_pwm (struct omap_dm_timer *timer,int def_on, +int toggle,int trigger) {u32 l;
@@ -659,7 +658,8 @@ int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,return 0 ; } -int omap_dm_timer_set_prescaler (struct omap_dm_timer *timer,int prescaler) +static int omap_dm_timer_set_prescaler (struct omap_dm_timer *timer, +int prescaler) {u32 l;
@@ -681,8 +681,8 @@ int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)return 0 ; } -int omap_dm_timer_set_int_enable (struct omap_dm_timer *timer, -unsigned int value) +static int omap_dm_timer_set_int_enable (struct omap_dm_timer *timer, +unsigned int value) {if (unlikely (!timer))return -EINVAL ;
@@ -704,7 +704,7 @@ int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,* * Disables the specified timer interrupts for a timer. */ -int omap_dm_timer_set_int_disable (struct omap_dm_timer *timer,u32 mask) +static int omap_dm_timer_set_int_disable (struct omap_dm_timer *timer,u32 mask) {u32 l = mask;
@@ -727,7 +727,7 @@ int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)return 0 ; } -unsigned int omap_dm_timer_read_status (struct omap_dm_timer *timer) +static unsigned int omap_dm_timer_read_status (struct omap_dm_timer *timer) {unsigned int l;
@@ -741,7 +741,7 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)return l; } -int omap_dm_timer_write_status (struct omap_dm_timer *timer,unsigned int value) +static int omap_dm_timer_write_status (struct omap_dm_timer *timer,unsigned int value) {if (unlikely (!timer ||pm_runtime_suspended (&timer->pdev->dev)))return -EINVAL ;
@@ -751,7 +751,7 @@ int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)return 0 ; } -unsigned int omap_dm_timer_read_counter (struct omap_dm_timer *timer) +static unsigned int omap_dm_timer_read_counter (struct omap_dm_timer *timer) {if (unlikely (!timer ||pm_runtime_suspended (&timer->pdev->dev))) {pr_err ("%s: timer not iavailable or enabled.\n" , __func__);
@@ -761,7 +761,7 @@ unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)return __omap_dm_timer_read_counter (timer, timer->posted); } -int omap_dm_timer_write_counter (struct omap_dm_timer *timer,unsigned int value) +static int omap_dm_timer_write_counter (struct omap_dm_timer *timer,unsigned int value) {if (unlikely (!timer ||pm_runtime_suspended (&timer->pdev->dev))) {pr_err ("%s: timer not available or enabled.\n" , __func__);
include/clocksource/dmtimer.h +0 -24
@@ -125,37 +125,13 @@ struct omap_dm_timer { };int omap_dm_timer_reserve_systimer (int id); -struct omap_dm_timer *omap_dm_timer_request (void ); -struct omap_dm_timer *omap_dm_timer_request_specific (int timer_id);struct omap_dm_timer *omap_dm_timer_request_by_cap (u32 cap); -struct omap_dm_timer *omap_dm_timer_request_by_node (struct device_node *np); -int omap_dm_timer_free (struct omap_dm_timer *timer); -void omap_dm_timer_enable (struct omap_dm_timer *timer); -void omap_dm_timer_disable (struct omap_dm_timer *timer);int omap_dm_timer_get_irq (struct omap_dm_timer *timer);u32 omap_dm_timer_modify_idlect_mask (u32 inputmask); -struct clk *omap_dm_timer_get_fclk (struct omap_dm_timer *timer);int omap_dm_timer_trigger (struct omap_dm_timer *timer); -int omap_dm_timer_start (struct omap_dm_timer *timer); -int omap_dm_timer_stop (struct omap_dm_timer *timer); - -int omap_dm_timer_set_source (struct omap_dm_timer *timer,int source); -int omap_dm_timer_set_load (struct omap_dm_timer *timer,int autoreload,unsigned int value); -int omap_dm_timer_set_load_start (struct omap_dm_timer *timer,int autoreload,unsigned int value); -int omap_dm_timer_set_match (struct omap_dm_timer *timer,int enable,unsigned int match); -int omap_dm_timer_set_pwm (struct omap_dm_timer *timer,int def_on,int toggle,int trigger); -int omap_dm_timer_set_prescaler (struct omap_dm_timer *timer,int prescaler); - -int omap_dm_timer_set_int_enable (struct omap_dm_timer *timer,unsigned int value); -int omap_dm_timer_set_int_disable (struct omap_dm_timer *timer,u32 mask); - -unsigned int omap_dm_timer_read_status (struct omap_dm_timer *timer); -int omap_dm_timer_write_status (struct omap_dm_timer *timer,unsigned int value); -unsigned int omap_dm_timer_read_counter (struct omap_dm_timer *timer); -int omap_dm_timer_write_counter (struct omap_dm_timer *timer,unsigned int value);int omap_dm_timers_active (void );
Signature · 1 line
2.15.1
PATCH 2/5 clocksource: timer-dm: Check prescaler value
In reply to Ladislav MichlInvalid value silently disables use of the prescaler. Use -1 explicitely for that purpose and error out on invalid value.
drivers/clocksource/timer-dm.c +2 -2
@@ -663,13 +663,13 @@ static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, {u32 l; -if (unlikely (!timer)) +if (unlikely (!timer) || prescaler <-1 || prescaler >7 )return -EINVAL ;omap_dm_timer_enable (timer); l =omap_dm_timer_read_reg (timer,OMAP_TIMER_CTRL_REG ); l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 <<2 )); -if (prescaler >=0x00 && prescaler <=0x07 ) { +if (prescaler >=0 ) { l |=OMAP_TIMER_CTRL_PRE ; l |= prescaler <<2 ; }
Signature · 1 line
2.15.1
PATCH 3/5 pwm: pwm-omap-dmtimer: Fix frequency when using prescaler
In reply to Ladislav MichlPrescaler setting is currently not taken into account. Fix that by introducing freq member variable and initialize it at device probe time. This also avoids frequency recomputing at each pwm configure time.
drivers/pwm/pwm-omap-dmtimer.c +42 -29
@@ -40,6 +40,7 @@ struct pwm_omap_dmtimer_chip {pwm_omap_dmtimer *dm_timer;struct omap_dm_timer_ops *pdata;struct platform_device *dm_timer_pdev; +unsigned long freq; }; static inline struct pwm_omap_dmtimer_chip *
@@ -48,9 +49,10 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) return container_of(chip, struct pwm_omap_dmtimer_chip, chip); } -static u32 pwm_omap_dmtimer_get_clock_cycles (unsigned long clk_rate,int ns) +static inline u32 +pwm_omap_dmtimer_get_clock_cycles (struct pwm_omap_dmtimer_chip *omap,int ns) { -return DIV_ROUND_CLOSEST_ULL ((u64 )clk_rate * ns,NSEC_PER_SEC ); +return DIV_ROUND_CLOSEST_ULL ((u64 )omap->freq * ns,NSEC_PER_SEC ); }static void pwm_omap_dmtimer_start (struct pwm_omap_dmtimer_chip *omap)
@@ -99,8 +101,6 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,struct pwm_omap_dmtimer_chip *omap =to_pwm_omap_dmtimer_chip (chip);u32 period_cycles, duty_cycles;u32 load_value, match_value; -struct clk *fclk; -unsigned long clk_rate;bool timer_active; dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n",
@@ -114,19 +114,6 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,return 0 ; } - fclk = omap->pdata->get_fclk (omap->dm_timer); -if (!fclk) { -dev_err (chip->dev,"invalid pmtimer fclk\n" ); - goto err_einval; - } - - clk_rate =clk_get_rate (fclk); -if (!clk_rate) { -dev_err (chip->dev,"invalid pmtimer fclk rate\n" ); - goto err_einval; - } - -dev_dbg (chip->dev,"clk rate: %luHz\n" , clk_rate);/* * Calculate the appropriate load and match values based on the
@@ -144,35 +131,35 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,* OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11 * AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6 */ - period_cycles =pwm_omap_dmtimer_get_clock_cycles (clk_rate, period_ns); - duty_cycles =pwm_omap_dmtimer_get_clock_cycles (clk_rate, duty_ns); + period_cycles =pwm_omap_dmtimer_get_clock_cycles (omap, period_ns); + duty_cycles =pwm_omap_dmtimer_get_clock_cycles (omap, duty_ns);if (period_cycles <2 ) {dev_info (chip->dev,"period %d ns too short for clock rate %lu Hz\n" , - period_ns, clk_rate); + period_ns, omap->freq); goto err_einval; }if (duty_cycles <1 ) {dev_dbg (chip->dev,"duty cycle %d ns is too short for clock rate %lu Hz\n" , - duty_ns, clk_rate); + duty_ns, omap->freq);dev_dbg (chip->dev,"using minimum of 1 clock cycle\n" ); duty_cycles =1 ; }else if (duty_cycles >= period_cycles) {dev_dbg (chip->dev,"duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n" , - duty_ns, period_ns, clk_rate); + duty_ns, period_ns, omap->freq);dev_dbg (chip->dev,"using maximum of 1 clock cycle less than period\n" ); duty_cycles = period_cycles -1 ; }dev_dbg (chip->dev,"effective duty cycle: %lld ns, period: %lld ns\n" ,DIV_ROUND_CLOSEST_ULL ((u64 )NSEC_PER_SEC * duty_cycles, - clk_rate), + omap->freq),DIV_ROUND_CLOSEST_ULL ((u64 )NSEC_PER_SEC * period_cycles, - clk_rate)); + omap->freq)); load_value = (DM_TIMER_MAX - period_cycles) +1 ; match_value = load_value + duty_cycles -1 ;
@@ -248,6 +235,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)struct dmtimer_platform_data *timer_pdata;struct omap_dm_timer_ops *pdata;pwm_omap_dmtimer *dm_timer; +struct clk *fclk;u32 v;int status;
@@ -311,12 +299,37 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)if (pm_runtime_active (&omap->dm_timer_pdev->dev)) omap->pdata->stop (omap->dm_timer); -if (!of_property_read_u32 (pdev->dev.of_node,"ti,prescaler" , &v)) - omap->pdata->set_prescaler (omap->dm_timer, v); -/* setup dmtimer clock source */ -if (!of_property_read_u32 (pdev->dev.of_node,"ti,clock-source" , &v)) - omap->pdata->set_source (omap->dm_timer, v); +if (!of_property_read_u32 (pdev->dev.of_node,"ti,clock-source" , &v)) { + status = omap->pdata->set_source (omap->dm_timer, v); +if (status) { +dev_err (&pdev->dev,"invalid clock-source\n" ); +return status; + } + } + + fclk = omap->pdata->get_fclk (omap->dm_timer); +if (!fclk) { +dev_err (&pdev->dev,"invalid fclk\n" ); +return -EINVAL ; + } + + omap->freq =clk_get_rate (fclk); +if (!omap->freq) { +dev_err (&pdev->dev,"invalid fclk rate\n" ); +return -EINVAL ; + } + +if (!of_property_read_u32 (pdev->dev.of_node,"ti,prescaler" , &v)) { + status = omap->pdata->set_prescaler (omap->dm_timer, v); +if (status) { +dev_err (&pdev->dev,"invalid prescaler\n" ); +return status; + } + omap->freq >>= v +1 ; + } + +dev_dbg (&pdev->dev,"clk rate: %luHz\n" , omap->freq); omap->chip.dev = &pdev->dev; omap->chip.ops = &pwm_omap_dmtimer_ops;
Signature · 1 line
2.15.1
PATCH 4/5 clocksource: timer-dm: Add event capture
In reply to Ladislav MichlImplement event capture functions.
drivers/clocksource/timer-dm.c +43 -0
@@ -633,6 +633,30 @@ static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,return 0 ; } +static int omap_dm_timer_set_capture (struct omap_dm_timer *timer, +int captmode,int edges) +{ +u32 l; + +if (unlikely (!timer)) +return -EINVAL ; + +omap_dm_timer_enable (timer); + l =omap_dm_timer_read_reg (timer,OMAP_TIMER_CTRL_REG ); + l &= ~(OMAP_TIMER_CTRL_CAPTMODE |OMAP_TIMER_CTRL_SCPWM | +OMAP_TIMER_CTRL_PT |OMAP_TIMER_CTRL_TCM_BOTHEDGES ); + l |=OMAP_TIMER_CTRL_GPOCFG ; +if (captmode) + l |=OMAP_TIMER_CTRL_CAPTMODE ; + l |= edges <<8 ; +omap_dm_timer_write_reg (timer,OMAP_TIMER_CTRL_REG , l); + +/* Save the context */ + timer->context.tclr = l; +omap_dm_timer_disable (timer); +return 0 ; +} +static int omap_dm_timer_set_pwm (struct omap_dm_timer *timer,int def_on,int toggle,int trigger) {
@@ -791,6 +815,22 @@ int omap_dm_timers_active(void)return 0 ; } +static int omap_dm_timer_read_capture (struct omap_dm_timer *timer, +unsigned int *reg, +unsigned int *reg2) +{ +if (unlikely (!timer ||pm_runtime_suspended (&timer->pdev->dev))) { +pr_err ("%s: timer not available or enabled.\n" , __func__); +return -EINVAL ; + } + + *reg =omap_dm_timer_read_reg (timer,OMAP_TIMER_CAPTURE_REG ); +if (reg2) + *reg2 =omap_dm_timer_read_reg (timer,OMAP_TIMER_CAPTURE2_REG ); + +return 0 ; +} +static const struct of_device_id omap_timer_match[];/**
@@ -939,11 +979,14 @@ static struct omap_dm_timer_ops dmtimer_ops = { .start = omap_dm_timer_start, .stop = omap_dm_timer_stop, .set_load = omap_dm_timer_set_load, + .set_load_start = omap_dm_timer_set_load_start, .set_match = omap_dm_timer_set_match, + .set_capture = omap_dm_timer_set_capture, .set_pwm = omap_dm_timer_set_pwm, .set_prescaler = omap_dm_timer_set_prescaler, .read_counter = omap_dm_timer_read_counter, .write_counter = omap_dm_timer_write_counter, + .read_capture = omap_dm_timer_read_capture, .read_status = omap_dm_timer_read_status, .write_status = omap_dm_timer_write_status, };
include/linux/platform_data/dmtimer-omap.h +8 -0
@@ -43,8 +43,12 @@ struct omap_dm_timer_ops {int (*set_load)(struct omap_dm_timer *timer,int autoreload,unsigned int value); +int (*set_load_start)(struct omap_dm_timer *timer,int autoreload, +unsigned int value);int (*set_match)(struct omap_dm_timer *timer,int enable,unsigned int match); +int (*set_capture)(struct omap_dm_timer *timer,int captmode, +int edges);int (*set_pwm)(struct omap_dm_timer *timer,int def_on,int toggle,int trigger);int (*set_prescaler)(struct omap_dm_timer *timer,int prescaler);
@@ -52,6 +56,10 @@ struct omap_dm_timer_ops {unsigned int (*read_counter)(struct omap_dm_timer *timer);int (*write_counter)(struct omap_dm_timer *timer,unsigned int value); + +int (*read_capture)(struct omap_dm_timer *timer,unsigned int *reg, +unsigned int *reg2); +unsigned int (*read_status)(struct omap_dm_timer *timer);int (*write_status)(struct omap_dm_timer *timer,unsigned int value);
Signature · 1 line
2.15.1
RFC 5/5 pwm: pwm-omap-dmtimer: Add capture functionality
In reply to Ladislav MichlCall for help:
For testing purposes pwm_evt pin is driven by a signal generator, in this example at 200us period and 80% duty cycle. Repeated testing gives:
$ cat /sys/class/pwm/pwmchip4/pwm0/capture 200000 160000 or: $ cat /sys/class/pwm/pwmchip4/pwm0/capture 200000 40000 Here it seems hardware can capture both edges, but I do not see a way how to tell it I want start from either low to high or high to low transition. Clues?
Also there is probably bug in the code as for shorter periods (here 10us) I'm getting:
$ cat /sys/class/pwm/pwmchip4/pwm0/capture omap_dm_timer_read_status: timer not available or enabled. 10000 2000 Unfortunately I do not see how it can happen in the code.
Any hints greatly appreciated, thank you...
drivers/pwm/pwm-omap-dmtimer.c +128 -1
@@ -17,7 +17,9 @@*/ #include <linux/clk.h> +#include <clocksource/dmtimer.h> #include <linux/err.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h>
@@ -30,17 +32,21 @@#include <linux/pwm.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/wait.h> #define DM_TIMER_LOAD_MIN 0xfffffffe#define DM_TIMER_MAX 0xffffffffstruct pwm_omap_dmtimer_chip {struct pwm_chip chip; +spinlock_t lock;struct mutex mutex; +wait_queue_head_t wait;pwm_omap_dmtimer *dm_timer;struct omap_dm_timer_ops *pdata;struct platform_device *dm_timer_pdev;unsigned long freq; +unsigned int ev_cnt, overflow, width; }; static inline struct pwm_omap_dmtimer_chip *
@@ -55,6 +61,22 @@ pwm_omap_dmtimer_get_clock_cycles(struct pwm_omap_dmtimer_chip *omap, int ns)return DIV_ROUND_CLOSEST_ULL ((u64 )omap->freq * ns,NSEC_PER_SEC ); } +static inline unsigned int +pwm_omap_dmtimer_ticks_to_ns (struct pwm_omap_dmtimer_chip *omap, +unsigned int ticks) +{ +return DIV_ROUND_CLOSEST_ULL ((u64 )ticks *NSEC_PER_SEC , omap->freq); +} + +static unsigned int pwm_omap_dmtimer_get_width (struct pwm_omap_dmtimer_chip *omap, +unsigned int c1,unsigned int c2) +{ +if (c1 <= c2) +return c2 - c1; + +return (c2 - omap->overflow) + (DM_TIMER_MAX - c1); +} +static void pwm_omap_dmtimer_start (struct pwm_omap_dmtimer_chip *omap) {/*
@@ -218,7 +240,101 @@ static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,return 0 ; } +static int pwm_omap_dmtimer_capture (struct pwm_chip *chip, +struct pwm_device *pwm, +struct pwm_capture *result, +unsigned long timeout) + +{ +int res; +unsigned long flags; +struct pwm_omap_dmtimer_chip *omap =to_pwm_omap_dmtimer_chip (chip); + +mutex_lock (&omap->mutex); + +if (pm_runtime_active (&omap->dm_timer_pdev->dev)) + omap->pdata->stop (omap->dm_timer); + +/* Let timer overflow every 1 second */ + omap->overflow =DM_TIMER_MAX - (1 * omap->freq); + timeout =msecs_to_jiffies (timeout); + result->period = result->duty_cycle =0 ; + +spin_lock_irqsave (&omap->lock, flags); + omap->pdata->set_int_enable (omap->dm_timer,OMAP_TIMER_INT_CAPTURE | +OMAP_TIMER_INT_OVERFLOW ); + omap->pdata->set_capture (omap->dm_timer,1 ,1 ); + omap->pdata->set_load_start (omap->dm_timer, true, omap->overflow); + omap->ev_cnt = omap->width =0 ; +spin_unlock_irqrestore (&omap->lock, flags); + + res =wait_event_interruptible_timeout (omap->wait, + omap->width >0 , timeout); +if (res >0 ) { +spin_lock_irqsave (&omap->lock, flags); + result->period = +pwm_omap_dmtimer_ticks_to_ns (omap, omap->width); + omap->pdata->stop (omap->dm_timer); + omap->pdata->set_capture (omap->dm_timer,1 ,3 ); + omap->pdata->start (omap->dm_timer); + omap->ev_cnt = omap->width =0 ; +spin_unlock_irqrestore (&omap->lock, flags); + + res =wait_event_interruptible_timeout (omap->wait, + omap->width >0 , + timeout); + } + +spin_lock_irqsave (&omap->lock, flags); + omap->pdata->stop (omap->dm_timer); + omap->pdata->set_int_disable (omap->dm_timer,OMAP_TIMER_INT_CAPTURE | +OMAP_TIMER_INT_OVERFLOW ); +spin_unlock_irqrestore (&omap->lock, flags); + +if (res ==0 ) { + res = -ETIMEDOUT ; + }else if (res >0 ) { + result->duty_cycle = +pwm_omap_dmtimer_ticks_to_ns (omap, omap->width); + res =0 ; + } + +mutex_unlock (&omap->mutex); + +return res; +} + +static irqreturn_t pwm_omap_dmtimer_irq (int irq,void *dev_id) +{ +u32 l; +unsigned int c1, c2; +unsigned long flags; +struct pwm_omap_dmtimer_chip *omap = dev_id; + +spin_lock_irqsave (&omap->lock, flags); + + l = omap->pdata->read_status (omap->dm_timer); +if (l &OMAP_TIMER_INT_CAPTURE ) { +if (!omap->width && omap->ev_cnt ==1 ) { + omap->pdata->read_capture (omap->dm_timer, &c1, &c2); + omap->width =pwm_omap_dmtimer_get_width (omap, c1, c2); +wake_up (&omap->wait); + } + omap->ev_cnt++; + } +/* TODO: handle overflow as well +if (l & OMAP_TIMER_INT_OVERFLOW) +overflow++; +*/ + omap->pdata->write_status (omap->dm_timer, l); + +spin_unlock_irqrestore (&omap->lock, flags); + +return IRQ_HANDLED ; +} +static const struct pwm_ops pwm_omap_dmtimer_ops = { + .capture = pwm_omap_dmtimer_capture, .enable = pwm_omap_dmtimer_enable, .disable = pwm_omap_dmtimer_disable, .config = pwm_omap_dmtimer_config,
@@ -237,7 +353,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)pwm_omap_dmtimer *dm_timer;struct clk *fclk;u32 v; -int status; +int irq, status; timer =of_parse_phandle (np,"ti,timers" ,0 );if (!timer)
@@ -338,7 +454,18 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) omap->chip.of_xlate = of_pwm_xlate_with_flags; omap->chip.of_pwm_n_cells =3 ; +spin_lock_init (&omap->lock);mutex_init (&omap->mutex); +init_waitqueue_head (&omap->wait); + + irq = omap->pdata->get_irq (omap->dm_timer); +if (irq <0 ) +return irq; + + status =devm_request_irq (&pdev->dev, irq, pwm_omap_dmtimer_irq,0 , +"event_capture" , omap); +if (status) +return status; status =pwmchip_add (&omap->chip);if (status <0 ) {
Signature · 1 line
2.15.1
RFC 5/5 pwm: pwm-omap-dmtimer: Add capture functionality
Quoted 3 lines · Ladislav Michl
Here it seems hardware can capture both edges, but I do not see a way how to tell it I want start from either low to high or high to low transition. Clues?
At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you probably know that already..
If you're having hard time getting things starting, maybe something like this helps:
stop timer in TCLR register
configure timer in TCLR
write some value to TLDR, maybe 0?
set ST bit in TCLR to start
Regards,
Tony
RFC 5/5 pwm: pwm-omap-dmtimer: Add capture functionality
Quoted 8 lines · Tony Lindgren
Quoted 3 lines · Ladislav Michl
Here it seems hardware can capture both edges, but I do not see a way how to tell it I want start from either low to high or high to low transition. Clues?
At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you probably know that already..
Quoted 7 lines
If you're having hard time getting things starting, maybe something like this helps:
stop timer in TCLR register
configure timer in TCLR
write some value to TLDR, maybe 0?
set ST bit in TCLR to start
Let me clarify it a bit more. I have no problem starting timer and capture events. I just didn't find a way how to tell hardware I want to start with for example rising edge, so rising edge goes to TCAR1 and failing edge to TCAR2. Substracting those gives pulse width.
ladis
RFC 5/5 pwm: pwm-omap-dmtimer: Add capture functionality
Quoted 17 lines · Ladislav Michl
Quoted 8 lines · Tony Lindgren
Quoted 3 lines · Ladislav Michl
Here it seems hardware can capture both edges, but I do not see a way how to tell it I want start from either low to high or high to low transition. Clues?
At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you probably know that already..
Quoted 7 lines
If you're having hard time getting things starting, maybe something like this helps:
stop timer in TCLR register
configure timer in TCLR
write some value to TLDR, maybe 0?
set ST bit in TCLR to start
Quoted 4 lines
Let me clarify it a bit more. I have no problem starting timer and capture events. I just didn't find a way how to tell hardware I want to start with for example rising edge, so rising edge goes to TCAR1 and failing edge to TCAR2. Substracting those gives pulse width.
Oh I see, yeah that would be cool :) Maybe you can first configure an interrupt to trigger on rising edge, then configure things for falling edge, then subtract..
Regards,
Tony
RFC 5/5 pwm: pwm-omap-dmtimer: Add capture functionality
Quoted 18 lines · Tony Lindgren
Quoted 17 lines · Ladislav Michl
Quoted 15 lines · Tony Lindgren
Quoted 3 lines · Ladislav Michl
Here it seems hardware can capture both edges, but I do not see a way how to tell it I want start from either low to high or high to low transition. Clues?
At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you probably know that already..
If you're having hard time getting things starting, maybe something like this helps:
stop timer in TCLR register
configure timer in TCLR
write some value to TLDR, maybe 0?
set ST bit in TCLR to start
Quoted 8 lines
Quoted 4 lines
Let me clarify it a bit more. I have no problem starting timer and capture events. I just didn't find a way how to tell hardware I want to start with for example rising edge, so rising edge goes to TCAR1 and failing edge to TCAR2. Substracting those gives pulse width.
Oh I see, yeah that would be cool :) Maybe you can first configure an interrupt to trigger on rising edge, then configure things for falling edge, then subtract..
That will work only for very long periods. I did test where value captured into TCAR was compared to value read from TCRR. Sometimes those differ by few hundreds microseconds...
I started this work to overcome GPIO edge interrupts latency, but so far I'm a bit dissapointed.
(also please read the code, reconfiguration is done to get period and duty cycle - but in case of duty cycle it is not clear whenever we are measuring pulse or space length)
ladis
cover Add capture functionality to OMAP pwm driver
In reply to Ladislav MichlQuoted 3 lines · Ladislav Michl
Tony, I wanted to do something easy to understand as a base for event capture interrupt, but either I do not understand hardware, or it indeed cannot do pulse capture.
Hmm OK. Presumably it works as it's been around for a long time, I thought any edge trigger will just sample the current counter value and that's it..
Regards,
Tony
cover Add capture functionality to OMAP pwm driver
Quoted 8 lines · Tony Lindgren
Quoted 3 lines · Ladislav Michl
Tony, I wanted to do something easy to understand as a base for event capture interrupt, but either I do not understand hardware, or it indeed cannot do pulse capture.
Hmm OK. Presumably it works as it's been around for a long time, I thought any edge trigger will just sample the current counter value and that's it..
Yes, indeed, _any_ edge will just sample the current counter value. So what can hardware say about duty cycle of this signal?
+-+ +-+ __| |________| |___
Depending time capture was started it will either trigger on first falling or first rising edge.
ladis