Message ID | 20221124170018.3150687-5-biju.das.jz@bp.renesas.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | None | expand |
Hi Biju, On Thu, Nov 24, 2022 at 6:00 PM Biju Das <biju.das.jz@bp.renesas.com> wrote: > Add RZ/G2L MTU3a counter driver. This IP supports the following > phase counting modes on MTU1 and MTU2 channels > > 1) 16-bit phase counting modes on MTU1 and MTU2 channels. > 2) 32-bit phase counting mode by cascading MTU1 and MTU2 channels. > > This patch adds 3 counter value channels. > count0: 16-bit phase counter value channel on MTU1 > count1: 16-bit phase counter value channel on MTU2 > count2: 32-bit phase counter value channel by cascading > MTU1 and MTU2 channels. > > The external input phase clock pin for the counter value channels > are as follows: > count0: "MTCLKA-MTCLKB" > count1: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > count2: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > > Use the sysfs variable "external_input_phase_clock_select" to select the > external input phase clock pin and "cascade_enable" to enable/disable > cascading of channels. > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> > --- > v6->v7: > * Updated commit description > * Added Register descriptions > * Opimized size of cache variable by using union > * Used test_bit() in rz_mtu3_is_counter_invalid() > * Replaced val->timer_mode in rz_mtu3_count_function_{read,write} > * Added TODO comment phase3 and phase5 modes. > * replaced if-else with ternary expression in rz_mtu3_count_direction_read() > * Used switch statement in rz_mtu3_count_ceiling_read to consistent with write > * Provided default case for all switch statement. > * Add mutex lock for avoiding races with other devices > * Updated comments in rz_mtu3_action_read > * Replaced COUNTER_COMP_DEVICE_BOOL->COUNTER_COMP_DEVICE_BOOL for > cascade_enable > * Replaced RZ_MTU3_GET_HW_CH->rz_mtu3_get_hw_ch > * Added rz_mtu3_get_ch() to get channels > * used rz_mtu3_shared_reg_update_bit for cascade_enable and > selecting phase input clock. > * Added rz_mtu3_is_counter_invalid() check in rz_mtu3_count_ceiling_read() Thanks for the update! > --- /dev/null > +++ b/drivers/counter/rz-mtu3-cnt.c > +static int rz_mtu3_initialize_counter(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + > + switch (id) { > + case RZ_MTU3_16_BIT_MTU1_CH: > + case RZ_MTU3_16_BIT_MTU2_CH: > + mutex_lock(&ch->lock); > + if (ch->function == RZ_MTU3_NORMAL) > + ch->function = RZ_MTU3_16BIT_PHASE_COUNTING; > + mutex_unlock(&ch->lock); > + > + if (ch->function != RZ_MTU3_16BIT_PHASE_COUNTING) > + return -EBUSY; I think having a request_channel() API would make this more readable, and avoid duplication (here and in the PWM driver). > + > + rz_mtu3_16bit_cnt_setting(counter, id); > + > + break; > + case RZ_MTU3_32_BIT_CH: > + /* > + * 32-bit phase counting need MTU1 and MTU2 to create 32-bit > + * cascade counter. > + */ > + mutex_lock(&ch1->lock); > + if (ch1->function == RZ_MTU3_NORMAL) > + ch1->function = RZ_MTU3_32BIT_PHASE_COUNTING; > + mutex_unlock(&ch1->lock); > + > + mutex_lock(&ch2->lock); > + if (ch2->function == RZ_MTU3_NORMAL) > + ch2->function = RZ_MTU3_32BIT_PHASE_COUNTING; > + mutex_unlock(&ch2->lock); > + > + if (ch1->function != RZ_MTU3_32BIT_PHASE_COUNTING || > + ch2->function != RZ_MTU3_32BIT_PHASE_COUNTING) > + return -EBUSY; Surely you need to release the channel you did obtain (if any)? > + > + rz_mtu3_32bit_cnt_setting(counter, id); > + break; > + default: > + /* should never reach this path */ > + return -EINVAL; > + } > + > + return 0; > +} > + > +static void rz_mtu3_terminate_counter(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + > + if (id == RZ_MTU3_32_BIT_CH) { > + mutex_lock(&ch1->lock); > + ch1->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch1->lock); Locking around a simple integer assignment doesn't do much. You might as well just use WRITE_ONCE(), to avoid reordering by the compiler. > + > + mutex_lock(&ch2->lock); > + ch2->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch2->lock); > + > + rz_mtu3_disable(ch2); > + rz_mtu3_disable(ch1); > + } else { > + mutex_lock(&ch->lock); > + ch->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch->lock); > + > + rz_mtu3_disable(ch); > + } > +} Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
On Thu, Nov 24, 2022 at 05:00:17PM +0000, Biju Das wrote: > Add RZ/G2L MTU3a counter driver. This IP supports the following > phase counting modes on MTU1 and MTU2 channels > > 1) 16-bit phase counting modes on MTU1 and MTU2 channels. > 2) 32-bit phase counting mode by cascading MTU1 and MTU2 channels. > > This patch adds 3 counter value channels. > count0: 16-bit phase counter value channel on MTU1 > count1: 16-bit phase counter value channel on MTU2 > count2: 32-bit phase counter value channel by cascading > MTU1 and MTU2 channels. > > The external input phase clock pin for the counter value channels > are as follows: > count0: "MTCLKA-MTCLKB" > count1: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > count2: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > > Use the sysfs variable "external_input_phase_clock_select" to select the > external input phase clock pin and "cascade_enable" to enable/disable > cascading of channels. > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> Hi Biju, Some comments inline below. > --- > v6->v7: > * Updated commit description > * Added Register descriptions > * Opimized size of cache variable by using union > * Used test_bit() in rz_mtu3_is_counter_invalid() > * Replaced val->timer_mode in rz_mtu3_count_function_{read,write} > * Added TODO comment phase3 and phase5 modes. > * replaced if-else with ternary expression in rz_mtu3_count_direction_read() > * Used switch statement in rz_mtu3_count_ceiling_read to consistent with write > * Provided default case for all switch statement. > * Add mutex lock for avoiding races with other devices > * Updated comments in rz_mtu3_action_read > * Replaced COUNTER_COMP_DEVICE_BOOL->COUNTER_COMP_DEVICE_BOOL for > cascade_enable > * Replaced RZ_MTU3_GET_HW_CH->rz_mtu3_get_hw_ch > * Added rz_mtu3_get_ch() to get channels > * used rz_mtu3_shared_reg_update_bit for cascade_enable and > selecting phase input clock. > * Added rz_mtu3_is_counter_invalid() check in rz_mtu3_count_ceiling_read() > v5->v6: > * Updated KConfig and commit description > * Sorted header > * Fixed RZ_MTU3_GET_HW_CH Macro for argument reuse 'id' - > possible side-effects? > * Replaced SET_RUNTIME_PM_OPS->DEFINE_RUNTIME_DEV_PM_OPS and removed > __maybe_unused from suspend/resume() > v4->v5: > * Updated the Kconfig with SoC vendor name > * Introduced rz_mtu3_is_counter_invalid() > * replaced pointer to an array of struct rz_mtu3_channel with > a simple pointer to struct rz_mtu3_channel. > * Added long_word_access_ctrl_mode sysfs entry for 16-bit and > 32-bit access > * Added external_input_phase_clock_select sysfs entry for > selecting input clocks. > * used preprocessor defines represent SIGNAL_{A,B,C,D}_ID instead of > signal ids. > v3->v4: > * There is no resource associated with "rz-mtu3-counter" compatible > and moved the code to mfd subsystem as it binds against "rz-mtu". > * Removed struct platform_driver rz_mtu3_cnt_driver. > * Updated commit description > * Updated Kconfig description > * Added macros RZ_MTU3_16_BIT_MTU{1,2}_CH for MTU1 and MTU2 channels > * Added RZ_MTU3_GET_HW_CH macro for getting channel ID. > * replaced priv->ch[id]->priv->ch[0] in rz_mtu3_count_read() > * Cached counter max values > * replaced cnt->tsr in rz_mtu3_count_direction_read() > * Added comments for RZ_MTU3_TCR_CCLR_NONE > * Replaced if with switch in rz_mtu3_initialize_counter() and > rz_mtu3_count_ceiling_write() > * Added locks in initialize, terminate and enable_read to prevent races. > * Updated rz_mtu3_action_read to take care of MTU2 signals. > * Added separate distinct array for each group of Synapse. > * Moved pm handling to parent. > > v1->v3: > * Modelled as a counter device supporting 3 counters(2 16-bit and > 32-bit) > * Add kernel-doc comments to document struct rz_mtu3_cnt > * Removed mmio variable from struct rz_mtu3_cnt > * Removed cnt local variable from rz_mtu3_count_read() > * Replaced -EINVAL->-ERANGE for out of range error conditions. > * Removed explicit cast from write functions. > * Removed local variable val from rz_mtu3_count_ceiling_read() > * Added lock for RMW for counter/ceiling updates. > * Added different synapses for counter0 and counter{1,2} > * Used ARRAY for assigning num_counts. > * Added PM runtime for managing clocks. > * Add MODULE_IMPORT_NS(COUNTER) to import the COUNTER namespace. > --- > drivers/counter/Kconfig | 11 + > drivers/counter/Makefile | 1 + > drivers/counter/rz-mtu3-cnt.c | 793 ++++++++++++++++++++++++++++++++++ > 3 files changed, 805 insertions(+) > create mode 100644 drivers/counter/rz-mtu3-cnt.c > > diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig > index d388bf26f4dc..daa352c7392d 100644 > --- a/drivers/counter/Kconfig > +++ b/drivers/counter/Kconfig > @@ -39,6 +39,17 @@ config INTERRUPT_CNT > To compile this driver as a module, choose M here: the > module will be called interrupt-cnt. > > +config RZ_MTU3_CNT > + tristate "Renesas RZ/G2L MTU3a counter driver" > + depends on RZ_MTU3 || COMPILE_TEST > + help > + Enable support for MTU3a counter driver found on Renesas RZ/G2L alike > + SoCs. This IP supports both 16-bit and 32-bit phase counting mode > + support. > + > + To compile this driver as a module, choose M here: the > + module will be called rz-mtu3-cnt. > + > config STM32_TIMER_CNT > tristate "STM32 Timer encoder counter driver" > depends on MFD_STM32_TIMERS || COMPILE_TEST > diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile > index b9a369e0d4fc..933fdd50b3e4 100644 > --- a/drivers/counter/Makefile > +++ b/drivers/counter/Makefile > @@ -8,6 +8,7 @@ counter-y := counter-core.o counter-sysfs.o counter-chrdev.o > > obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o > obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o > +obj-$(CONFIG_RZ_MTU3_CNT) += rz-mtu3-cnt.o > obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o > obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o > obj-$(CONFIG_TI_EQEP) += ti-eqep.o > diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c > new file mode 100644 > index 000000000000..0b0c5f9b2993 > --- /dev/null > +++ b/drivers/counter/rz-mtu3-cnt.c > @@ -0,0 +1,793 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Renesas RZ/G2L MTU3a Counter driver > + * > + * Copyright (C) 2022 Renesas Electronics Corporation > + */ > + > +#include <linux/clk.h> > +#include <linux/counter.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/types.h> > + > +#include <clocksource/rz-mtu3.h> > + > +/* > + * Register descriptions > + * TSR: Timer Status Register > + * TMDR1: Timer Mode Register 1 > + * TMDR3: Timer Mode Register 3 > + * TIOR: Timer I/O Control Register > + * TCR: Timer Control Register > + * TCNT: Timer Counter > + * TGRA: Timer general register A > + * TCNTLW: Timer Longword Counter > + * TGRALW: Timer longword general register A > + */ > + > +#define RZ_MTU3_TSR_TCFD BIT(7) /* Count Direction Flag */ > + > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_1 (4) /* Phase counting mode 1 */ > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_2 (5) /* Phase counting mode 2 */ > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_3 (6) /* Phase counting mode 3 */ > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_4 (7) /* Phase counting mode 4 */ > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_5 (9) /* Phase counting mode 5 */ > +#define RZ_MTU3_TMDR1_PH_CNT_MODE_MASK (0xf) > + > +/* > + * LWA: MTU1/MTU2 Combination Longword Access Control > + * 0: 16-bit, 1: 32-bit > + */ > +#define RZ_MTU3_TMDR3_LWA (0) > + > +/* > + * PHCKSEL: External Input Phase Clock Select > + * 0: MTCLKA and MTCLKB, 1: MTCLKC and MTCLKD > + */ > +#define RZ_MTU3_TMDR3_PHCKSEL (1) > + > +#define RZ_MTU3_16_BIT_MTU1_CH (0) > +#define RZ_MTU3_16_BIT_MTU2_CH (1) > +#define RZ_MTU3_32_BIT_CH (2) > + > +#define RZ_MTU3_TIOR_NO_OUTPUT (0) /* Output prohibited */ > +#define RZ_MTU3_TIOR_IC_BOTH (10) /* Input capture at both edges */ > + > +#define SIGNAL_A_ID (0) > +#define SIGNAL_B_ID (1) > +#define SIGNAL_C_ID (2) > +#define SIGNAL_D_ID (3) > + > +#define RZ_MTU3_MAX_HW_CNTR_CHANNELS (2) > + > +/** > + * struct rz_mtu3_cnt - MTU3 counter private data > + * > + * @clk: MTU3 module clock > + * @lock: Lock to prevent concurrent access for ceiling and count > + * @ch: HW channels for the counters > + * @mtu_16bit_max: Cache for 16-bit counters > + * @mtu_32bit_max: Cache for 32-bit counters > + */ > +struct rz_mtu3_cnt { > + struct clk *clk; > + struct mutex lock; > + struct rz_mtu3_channel *ch; > + union { > + u16 mtu_16bit_max[RZ_MTU3_MAX_HW_CNTR_CHANNELS]; > + u32 mtu_32bit_max; > + }; > +}; > + > +static const enum counter_function rz_mtu3_count_functions[] = { > + COUNTER_FUNCTION_QUADRATURE_X4, > + COUNTER_FUNCTION_PULSE_DIRECTION, > + COUNTER_FUNCTION_QUADRATURE_X2_B, > +}; > + > +static inline size_t rz_mtu3_get_hw_ch(const size_t id) > +{ > + return (id == RZ_MTU3_32_BIT_CH) ? 0 : id; > +} > + > +static inline struct rz_mtu3_channel * > +rz_mtu3_get_ch(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + const size_t ch_id = rz_mtu3_get_hw_ch(id); > + > + return &priv->ch[ch_id]; > +} > + > +static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + unsigned long tmdr; > + > + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); > + if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) > + return false; > + > + if (id != RZ_MTU3_32_BIT_CH && !test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) > + return false; > + > + return true; > +} > + > +static int rz_mtu3_count_read(struct counter_device *counter, > + struct counter_count *count, u64 *val) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + > + if (rz_mtu3_is_counter_invalid(counter, count->id)) > + return -EINVAL; So what rz_mtu3_is_counter_invalid() does is check if the respective Count component is available for the 16-bit or 32-bit operation mode. It'll make sense to return -EBUSY in these cases rather than -EINVAL, because semantically the user request here isn't due to an invalid argument by the user -- rather the request can't be fulfilled because the device is currently busy in the other operation mode. > + > + if (count->id == RZ_MTU3_32_BIT_CH) > + *val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW); > + else > + *val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT); It looks like you have a race condition here: after passing the rz_mtu3_is_counter_invalid() check earlier, the operation mode could change before you perform the subsequent channel read here. You should protect this callback with a lock to make sure the operation mode isn't changed by rz_mtu3_cascade_enable_set() or otherwise. > + > + return 0; > +} > + > +static int rz_mtu3_count_write(struct counter_device *counter, > + struct counter_count *count, const u64 val) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); > + u32 ceiling; > + > + if (rz_mtu3_is_counter_invalid(counter, count->id)) > + return -EINVAL; > + > + mutex_lock(&priv->lock); Move this lock above the rz_mtu3_is_counter_invalid() check for the same reason as in the count_read() callback. > + if (count->id == RZ_MTU3_32_BIT_CH) > + ceiling = priv->mtu_32bit_max; > + else > + ceiling = priv->mtu_16bit_max[ch_id]; > + > + if (count->id == RZ_MTU3_32_BIT_CH) > + rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val); > + else > + rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val); > + > + mutex_unlock(&priv->lock); > + > + return 0; > +} > + > +static int rz_mtu3_count_function_read(struct counter_device *counter, > + struct counter_count *count, > + enum counter_function *function) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + u8 timer_mode; > + > + timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); > + > + switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { > + case RZ_MTU3_TMDR1_PH_CNT_MODE_1: > + *function = COUNTER_FUNCTION_QUADRATURE_X4; > + break; > + case RZ_MTU3_TMDR1_PH_CNT_MODE_2: > + *function = COUNTER_FUNCTION_PULSE_DIRECTION; > + break; > + case RZ_MTU3_TMDR1_PH_CNT_MODE_4: > + *function = COUNTER_FUNCTION_QUADRATURE_X2_B; > + break; > + default: > + /* > + * TODO: > + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3 > + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5 > + */ > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int rz_mtu3_count_function_write(struct counter_device *counter, > + struct counter_count *count, > + enum counter_function function) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + u8 timer_mode; > + > + switch (function) { > + case COUNTER_FUNCTION_QUADRATURE_X4: > + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_1; > + break; > + case COUNTER_FUNCTION_PULSE_DIRECTION: > + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_2; > + break; > + case COUNTER_FUNCTION_QUADRATURE_X2_B: > + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_4; > + break; > + default: > + /* > + * TODO: > + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3 > + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5 > + */ > + return -EINVAL; > + } > + > + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); > + > + return 0; > +} > + > +static int rz_mtu3_count_direction_read(struct counter_device *counter, > + struct counter_count *count, > + enum counter_count_direction *direction) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + u8 tsr; > + > + tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); > + *direction = (tsr & RZ_MTU3_TSR_TCFD) ? > + COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; > + > + return 0; > +} > + > +static int rz_mtu3_count_ceiling_read(struct counter_device *counter, > + struct counter_count *count, > + u64 *ceiling) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); > + > + if (rz_mtu3_is_counter_invalid(counter, count->id)) > + return -EINVAL; > + > + switch (count->id) { > + case RZ_MTU3_16_BIT_MTU1_CH: > + case RZ_MTU3_16_BIT_MTU2_CH: > + *ceiling = priv->mtu_16bit_max[ch_id]; > + break; > + case RZ_MTU3_32_BIT_CH: > + *ceiling = priv->mtu_32bit_max; > + break; > + default: > + /* should never reach this path */ > + return -EINVAL; > + } > + > + if (count->id == RZ_MTU3_32_BIT_CH) > + *ceiling = priv->mtu_32bit_max; > + else > + *ceiling = priv->mtu_16bit_max[ch_id]; > + > + return 0; > +} Protect the rz_mtu3_count_ceiling_read() function with a lock to make sure the cascade operation mode doesn't not change and that the priv data structure accesses don't race when they are changed in the ceiling_write() callback. > + > +static int rz_mtu3_count_ceiling_write(struct counter_device *counter, > + struct counter_count *count, > + u64 ceiling) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); > + > + if (rz_mtu3_is_counter_invalid(counter, count->id)) > + return -EINVAL; > + > + switch (count->id) { > + case RZ_MTU3_16_BIT_MTU1_CH: > + case RZ_MTU3_16_BIT_MTU2_CH: > + if (ceiling > U16_MAX) > + return -ERANGE; > + priv->mtu_16bit_max[ch_id] = ceiling; > + break; > + case RZ_MTU3_32_BIT_CH: > + if (ceiling > U32_MAX) > + return -ERANGE; > + priv->mtu_32bit_max = ceiling; > + break; > + default: > + /* should never reach this path */ > + return -EINVAL; > + } > + > + mutex_lock(&priv->lock); > + if (count->id == RZ_MTU3_32_BIT_CH) > + rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling); > + else > + rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling); > + > + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); > + mutex_unlock(&priv->lock); > + > + return 0; > +} Same comments as for the ceiling_read() callback. > + > +static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + > + /* Phase counting mode 1 is used as default in initialization. */ > + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1); > + > + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); > + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TIOR, RZ_MTU3_TIOR_IC_BOTH); > + > + rz_mtu3_enable(ch1); > + rz_mtu3_enable(ch2); > +} > + > +static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > + > + /* Phase counting mode 1 is used as default in initialization. */ > + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1); > + > + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); > + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TIOR, RZ_MTU3_TIOR_NO_OUTPUT); > + rz_mtu3_enable(ch); > +} > + > +static int rz_mtu3_initialize_counter(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + > + switch (id) { > + case RZ_MTU3_16_BIT_MTU1_CH: > + case RZ_MTU3_16_BIT_MTU2_CH: > + mutex_lock(&ch->lock); > + if (ch->function == RZ_MTU3_NORMAL) > + ch->function = RZ_MTU3_16BIT_PHASE_COUNTING; > + mutex_unlock(&ch->lock); > + > + if (ch->function != RZ_MTU3_16BIT_PHASE_COUNTING) > + return -EBUSY; > + > + rz_mtu3_16bit_cnt_setting(counter, id); > + > + break; > + case RZ_MTU3_32_BIT_CH: > + /* > + * 32-bit phase counting need MTU1 and MTU2 to create 32-bit > + * cascade counter. > + */ > + mutex_lock(&ch1->lock); > + if (ch1->function == RZ_MTU3_NORMAL) > + ch1->function = RZ_MTU3_32BIT_PHASE_COUNTING; > + mutex_unlock(&ch1->lock); > + > + mutex_lock(&ch2->lock); > + if (ch2->function == RZ_MTU3_NORMAL) > + ch2->function = RZ_MTU3_32BIT_PHASE_COUNTING; > + mutex_unlock(&ch2->lock); > + > + if (ch1->function != RZ_MTU3_32BIT_PHASE_COUNTING || > + ch2->function != RZ_MTU3_32BIT_PHASE_COUNTING) > + return -EBUSY; > + > + rz_mtu3_32bit_cnt_setting(counter, id); > + break; > + default: > + /* should never reach this path */ > + return -EINVAL; > + } > + > + return 0; > +} > + > +static void rz_mtu3_terminate_counter(struct counter_device *counter, int id) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + > + if (id == RZ_MTU3_32_BIT_CH) { > + mutex_lock(&ch1->lock); > + ch1->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch1->lock); > + > + mutex_lock(&ch2->lock); > + ch2->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch2->lock); > + > + rz_mtu3_disable(ch2); > + rz_mtu3_disable(ch1); > + } else { > + mutex_lock(&ch->lock); > + ch->function = RZ_MTU3_NORMAL; > + mutex_unlock(&ch->lock); > + > + rz_mtu3_disable(ch); > + } > +} > + > +static int rz_mtu3_count_enable_read(struct counter_device *counter, > + struct counter_count *count, u8 *enable) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + > + if (count->id == RZ_MTU3_32_BIT_CH) { > + mutex_lock(&priv->lock); > + *enable = rz_mtu3_is_enabled(ch1) && > + rz_mtu3_is_enabled(ch2); > + mutex_unlock(&priv->lock); > + } else { > + *enable = rz_mtu3_is_enabled(ch); > + } > + > + return 0; > +} > + > +static int rz_mtu3_count_enable_write(struct counter_device *counter, > + struct counter_count *count, u8 enable) > +{ > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + int ret = 0; > + > + if (enable) { > + pm_runtime_get_sync(ch->dev); > + mutex_lock(&priv->lock); > + ret = rz_mtu3_initialize_counter(counter, count->id); > + mutex_unlock(&priv->lock); > + } else { > + mutex_lock(&priv->lock); > + rz_mtu3_terminate_counter(counter, count->id); > + mutex_unlock(&priv->lock); > + pm_runtime_put(ch->dev); > + } > + > + return ret; > +} > + > +static int rz_mtu3_cascade_enable_get(struct counter_device *counter, > + u8 *cascade_enable) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + unsigned long tmdr; > + > + pm_runtime_get_sync(priv->ch->dev); > + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); > + *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); > + pm_runtime_put(priv->ch->dev); > + > + return 0; > +} > + > +static int rz_mtu3_cascade_enable_set(struct counter_device *counter, > + u8 cascade_enable) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + > + pm_runtime_get_sync(priv->ch->dev); > + rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, > + RZ_MTU3_TMDR3_LWA, cascade_enable); > + pm_runtime_put(priv->ch->dev); > + > + return 0; > +} You need to lock rz_mtu3_cascade_enable_set() to make sure the other callbacks don't try to read the LWA state while you're updating it. > + > +static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *counter, > + u32 *ext_input_phase_clock_select) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + unsigned long tmdr; > + > + pm_runtime_get_sync(priv->ch->dev); > + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); > + *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); > + pm_runtime_put(priv->ch->dev); > + > + return 0; > +} > + > +static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *counter, > + u32 ext_input_phase_clock_select) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + > + pm_runtime_get_sync(priv->ch->dev); > + rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, > + RZ_MTU3_TMDR3_PHCKSEL, > + ext_input_phase_clock_select); > + pm_runtime_put(priv->ch->dev); > + > + return 0; > +} You need to lock rz_mtu3_ext_input_phase_clock_select_set() to ensure the other callbacks don't try to read the PHCKSEL state while you're updating it. > + > +static struct counter_comp rz_mtu3_count_ext[] = { > + COUNTER_COMP_DIRECTION(rz_mtu3_count_direction_read), > + COUNTER_COMP_ENABLE(rz_mtu3_count_enable_read, > + rz_mtu3_count_enable_write), > + COUNTER_COMP_CEILING(rz_mtu3_count_ceiling_read, > + rz_mtu3_count_ceiling_write), > +}; > + > +static const enum counter_synapse_action rz_mtu3_synapse_actions[] = { > + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, > + COUNTER_SYNAPSE_ACTION_RISING_EDGE, > + COUNTER_SYNAPSE_ACTION_NONE, > +}; > + > +static int rz_mtu3_action_read(struct counter_device *counter, > + struct counter_count *count, > + struct counter_synapse *synapse, > + enum counter_synapse_action *action) > +{ > + struct rz_mtu3_cnt *const priv = counter_priv(counter); > + enum counter_function function; > + bool mtclkc_mtclkd; > + unsigned long tmdr; > + int err; > + > + err = rz_mtu3_count_function_read(counter, count, &function); You have a race here if rz_mtu3_count_function_write() is called and changes the Count function. Protect your rz_mtu3_action_read() function with a lock to make sure the Count function doesn't change until you're finished. > + if (err) > + return err; > + > + /* Default action mode */ > + *action = COUNTER_SYNAPSE_ACTION_NONE; > + > + if (count->id != RZ_MTU3_16_BIT_MTU1_CH) { > + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); > + mtclkc_mtclkd = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); > + if (mtclkc_mtclkd && (synapse->signal->id == SIGNAL_A_ID || > + synapse->signal->id == SIGNAL_B_ID)) > + return -EINVAL; > + > + if (!mtclkc_mtclkd && (synapse->signal->id == SIGNAL_C_ID || > + synapse->signal->id == SIGNAL_D_ID)) > + return -EINVAL; These two return -EINVAL should simply be a return 0 to exit early. These are valid Synapses -- they just happen to be in a COUNTER_SYNAPSE_ACTION_NONE state until the respective phase clock is selected for them. William Breathitt Gray > + } > + > + switch (function) { > + case COUNTER_FUNCTION_PULSE_DIRECTION: > + /* > + * Rising edges on signal A (signal C) updates the respective > + * count. The input level of signal B (signal D) determines > + * direction. > + */ > + if (synapse->signal->id == SIGNAL_A_ID || > + synapse->signal->id == SIGNAL_C_ID) > + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; > + break; > + case COUNTER_FUNCTION_QUADRATURE_X2_B: > + /* > + * Any state transition on quadrature pair signal B (signal D) > + * updates the respective count. > + */ > + if (synapse->signal->id == SIGNAL_B_ID || > + synapse->signal->id == SIGNAL_D_ID) > + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; > + break; > + case COUNTER_FUNCTION_QUADRATURE_X4: > + /* counts up/down on both edges of A (C) and B (D) signal */ > + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; > + break; > + default: > + /* should never reach this path */ > + return -EINVAL; > + } > + > + return 0; > +} > + > +static const struct counter_ops rz_mtu3_cnt_ops = { > + .count_read = rz_mtu3_count_read, > + .count_write = rz_mtu3_count_write, > + .function_read = rz_mtu3_count_function_read, > + .function_write = rz_mtu3_count_function_write, > + .action_read = rz_mtu3_action_read, > +}; > + > +#define RZ_MTU3_PHASE_SIGNAL(_id, _name) { \ > + .id = (_id), \ > + .name = (_name), \ > +} > + > +static struct counter_signal rz_mtu3_signals[] = { > + RZ_MTU3_PHASE_SIGNAL(SIGNAL_A_ID, "MTU1 MTCLKA"), > + RZ_MTU3_PHASE_SIGNAL(SIGNAL_B_ID, "MTU1 MTCLKB"), > + RZ_MTU3_PHASE_SIGNAL(SIGNAL_C_ID, "MTU2 MTCLKC"), > + RZ_MTU3_PHASE_SIGNAL(SIGNAL_D_ID, "MTU2 MTCLKD"), > +}; > + > +static struct counter_synapse rz_mtu3_mtu1_count_synapses[] = { > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals, > + }, > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals + 1, > + } > +}; > + > +static struct counter_synapse rz_mtu3_mtu2_count_synapses[] = { > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals, > + }, > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals + 1, > + }, > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals + 2, > + }, > + { > + .actions_list = rz_mtu3_synapse_actions, > + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), > + .signal = rz_mtu3_signals + 3, > + } > +}; > + > +static struct counter_count rz_mtu3_counts[] = { > + { > + .id = RZ_MTU3_16_BIT_MTU1_CH, > + .name = "Channel 1 Count", > + .functions_list = rz_mtu3_count_functions, > + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), > + .synapses = rz_mtu3_mtu1_count_synapses, > + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu1_count_synapses), > + .ext = rz_mtu3_count_ext, > + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), > + }, > + { > + .id = RZ_MTU3_16_BIT_MTU2_CH, > + .name = "Channel 2 Count", > + .functions_list = rz_mtu3_count_functions, > + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), > + .synapses = rz_mtu3_mtu2_count_synapses, > + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu2_count_synapses), > + .ext = rz_mtu3_count_ext, > + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), > + }, > + { > + .id = RZ_MTU3_32_BIT_CH, > + .name = "Channel 1 and 2 (combined) Count", > + .functions_list = rz_mtu3_count_functions, > + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), > + .synapses = rz_mtu3_mtu2_count_synapses, > + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu2_count_synapses), > + .ext = rz_mtu3_count_ext, > + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), > + } > +}; > + > +static const char *const rz_mtu3_ext_input_phase_clock_select[] = { > + "MTCLKA-MTCLKB", > + "MTCLKC-MTCLKD", > +}; > + > +static DEFINE_COUNTER_ENUM(rz_mtu3_ext_input_phase_clock_select_enum, > + rz_mtu3_ext_input_phase_clock_select); > + > +static struct counter_comp rz_mtu3_device_ext[] = { > + COUNTER_COMP_DEVICE_BOOL("cascade_enable", > + rz_mtu3_cascade_enable_get, > + rz_mtu3_cascade_enable_set), > + COUNTER_COMP_DEVICE_ENUM("external_input_phase_clock_select", > + rz_mtu3_ext_input_phase_clock_select_get, > + rz_mtu3_ext_input_phase_clock_select_set, > + rz_mtu3_ext_input_phase_clock_select_enum), > +}; > + > +static int rz_mtu3_cnt_pm_runtime_suspend(struct device *dev) > +{ > + struct clk *const clk = dev_get_drvdata(dev); > + > + clk_disable_unprepare(clk); > + > + return 0; > +} > + > +static int rz_mtu3_cnt_pm_runtime_resume(struct device *dev) > +{ > + struct clk *const clk = dev_get_drvdata(dev); > + > + clk_prepare_enable(clk); > + > + return 0; > +} > + > +static DEFINE_RUNTIME_DEV_PM_OPS(rz_mtu3_cnt_pm_ops, > + rz_mtu3_cnt_pm_runtime_suspend, > + rz_mtu3_cnt_pm_runtime_resume, NULL); > + > +static void rz_mtu3_cnt_pm_disable(void *data) > +{ > + struct device *dev = data; > + > + pm_runtime_disable(dev); > + pm_runtime_set_suspended(dev); > +} > + > +static int rz_mtu3_cnt_probe(struct platform_device *pdev) > +{ > + struct rz_mtu3 *ddata = dev_get_drvdata(pdev->dev.parent); > + struct device *dev = &pdev->dev; > + struct counter_device *counter; > + struct rz_mtu3_channel *ch; > + struct rz_mtu3_cnt *priv; > + unsigned int i; > + int ret; > + > + counter = devm_counter_alloc(dev, sizeof(*priv)); > + if (!counter) > + return -ENOMEM; > + > + priv = counter_priv(counter); > + priv->clk = ddata->clk; > + priv->mtu_32bit_max = U32_MAX; > + priv->ch = &ddata->channels[RZ_MTU1]; > + ch = &priv->ch[0]; > + for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++) { > + ch->dev = dev; > + priv->mtu_16bit_max[i] = U16_MAX; > + ch++; > + } > + > + mutex_init(&priv->lock); > + platform_set_drvdata(pdev, priv->clk); > + clk_prepare_enable(priv->clk); > + pm_runtime_set_active(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + ret = devm_add_action_or_reset(&pdev->dev, rz_mtu3_cnt_pm_disable, dev); > + if (ret < 0) > + goto disable_clock; > + > + counter->name = dev_name(dev); > + counter->parent = dev; > + counter->ops = &rz_mtu3_cnt_ops; > + counter->counts = rz_mtu3_counts; > + counter->num_counts = ARRAY_SIZE(rz_mtu3_counts); > + counter->signals = rz_mtu3_signals; > + counter->num_signals = ARRAY_SIZE(rz_mtu3_signals); > + counter->ext = rz_mtu3_device_ext; > + counter->num_ext = ARRAY_SIZE(rz_mtu3_device_ext); > + > + /* Register Counter device */ > + ret = devm_counter_add(dev, counter); > + if (ret < 0) { > + dev_err_probe(dev, ret, "Failed to add counter\n"); > + goto disable_clock; > + } > + > + return 0; > + > +disable_clock: > + clk_disable_unprepare(priv->clk); > + > + return ret; > +} > + > +static struct platform_driver rz_mtu3_cnt_driver = { > + .probe = rz_mtu3_cnt_probe, > + .driver = { > + .name = "rz-mtu3-counter", > + .pm = pm_ptr(&rz_mtu3_cnt_pm_ops), > + }, > +}; > +module_platform_driver(rz_mtu3_cnt_driver); > + > +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); > +MODULE_ALIAS("platform:rz-mtu3-counter"); > +MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a counter driver"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS(COUNTER); > -- > 2.25.1 >
Hi Geert, Thanks for the feedback. > Subject: Re: [PATCH v7 4/5] counter: Add Renesas RZ/G2L MTU3a counter > driver > > Hi Biju, > > On Thu, Nov 24, 2022 at 6:00 PM Biju Das <biju.das.jz@bp.renesas.com> > wrote: > > Add RZ/G2L MTU3a counter driver. This IP supports the following phase > > counting modes on MTU1 and MTU2 channels > > > > 1) 16-bit phase counting modes on MTU1 and MTU2 channels. > > 2) 32-bit phase counting mode by cascading MTU1 and MTU2 channels. > > > > This patch adds 3 counter value channels. > > count0: 16-bit phase counter value channel on MTU1 > > count1: 16-bit phase counter value channel on MTU2 > > count2: 32-bit phase counter value channel by cascading > > MTU1 and MTU2 channels. > > > > The external input phase clock pin for the counter value channels are > > as follows: > > count0: "MTCLKA-MTCLKB" > > count1: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > > count2: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" > > > > Use the sysfs variable "external_input_phase_clock_select" to select > > the external input phase clock pin and "cascade_enable" to > > enable/disable cascading of channels. > > > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> > > --- > > v6->v7: > > * Updated commit description > > * Added Register descriptions > > * Opimized size of cache variable by using union > > * Used test_bit() in rz_mtu3_is_counter_invalid() > > * Replaced val->timer_mode in rz_mtu3_count_function_{read,write} > > * Added TODO comment phase3 and phase5 modes. > > * replaced if-else with ternary expression in > > rz_mtu3_count_direction_read() > > * Used switch statement in rz_mtu3_count_ceiling_read to consistent > > with write > > * Provided default case for all switch statement. > > * Add mutex lock for avoiding races with other devices > > * Updated comments in rz_mtu3_action_read > > * Replaced COUNTER_COMP_DEVICE_BOOL->COUNTER_COMP_DEVICE_BOOL for > > cascade_enable > > * Replaced RZ_MTU3_GET_HW_CH->rz_mtu3_get_hw_ch > > * Added rz_mtu3_get_ch() to get channels > > * used rz_mtu3_shared_reg_update_bit for cascade_enable and > > selecting phase input clock. > > * Added rz_mtu3_is_counter_invalid() check in > > rz_mtu3_count_ceiling_read() > > Thanks for the update! > > > --- /dev/null > > +++ b/drivers/counter/rz-mtu3-cnt.c > > > +static int rz_mtu3_initialize_counter(struct counter_device *counter, > > +int id) { > > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, > > +1); > > + > > + switch (id) { > > + case RZ_MTU3_16_BIT_MTU1_CH: > > + case RZ_MTU3_16_BIT_MTU2_CH: > > + mutex_lock(&ch->lock); > > + if (ch->function == RZ_MTU3_NORMAL) > > + ch->function = RZ_MTU3_16BIT_PHASE_COUNTING; > > + mutex_unlock(&ch->lock); > > + > > + if (ch->function != RZ_MTU3_16BIT_PHASE_COUNTING) > > + return -EBUSY; > > I think having a request_channel() API would make this more readable, and > avoid duplication (here and in the PWM driver). Agreed. > > > + > > + rz_mtu3_16bit_cnt_setting(counter, id); > > + > > + break; > > + case RZ_MTU3_32_BIT_CH: > > + /* > > + * 32-bit phase counting need MTU1 and MTU2 to create > 32-bit > > + * cascade counter. > > + */ > > + mutex_lock(&ch1->lock); > > + if (ch1->function == RZ_MTU3_NORMAL) > > + ch1->function = RZ_MTU3_32BIT_PHASE_COUNTING; > > + mutex_unlock(&ch1->lock); > > + > > + mutex_lock(&ch2->lock); > > + if (ch2->function == RZ_MTU3_NORMAL) > > + ch2->function = RZ_MTU3_32BIT_PHASE_COUNTING; > > + mutex_unlock(&ch2->lock); > > + > > + if (ch1->function != RZ_MTU3_32BIT_PHASE_COUNTING || > > + ch2->function != RZ_MTU3_32BIT_PHASE_COUNTING) > > + return -EBUSY; > > Surely you need to release the channel you did obtain (if any)? Agreed. > > > + > > + rz_mtu3_32bit_cnt_setting(counter, id); > > + break; > > + default: > > + /* should never reach this path */ > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +static void rz_mtu3_terminate_counter(struct counter_device *counter, > > +int id) { > > + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); > > + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); > > + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, > > +1); > > + > > + if (id == RZ_MTU3_32_BIT_CH) { > > + mutex_lock(&ch1->lock); > > + ch1->function = RZ_MTU3_NORMAL; > > + mutex_unlock(&ch1->lock); > > Locking around a simple integer assignment doesn't do much. > You might as well just use WRITE_ONCE(), to avoid reordering by the > compiler. We don't need this anymore once we have request and release calls as that function updates channel state by holding channel lock. Cheers Biju > > > + > > + mutex_lock(&ch2->lock); > > + ch2->function = RZ_MTU3_NORMAL; > > + mutex_unlock(&ch2->lock); > > + > > + rz_mtu3_disable(ch2); > > + rz_mtu3_disable(ch1); > > + } else { > > + mutex_lock(&ch->lock); > > + ch->function = RZ_MTU3_NORMAL; > > + mutex_unlock(&ch->lock); > > + > > + rz_mtu3_disable(ch); > > + } > > +} > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux- > m68k.org > > In personal conversations with technical people, I call myself a hacker. > But when I'm talking to journalists I just say "programmer" or something > like that. > -- Linus Torvalds
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index d388bf26f4dc..daa352c7392d 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -39,6 +39,17 @@ config INTERRUPT_CNT To compile this driver as a module, choose M here: the module will be called interrupt-cnt. +config RZ_MTU3_CNT + tristate "Renesas RZ/G2L MTU3a counter driver" + depends on RZ_MTU3 || COMPILE_TEST + help + Enable support for MTU3a counter driver found on Renesas RZ/G2L alike + SoCs. This IP supports both 16-bit and 32-bit phase counting mode + support. + + To compile this driver as a module, choose M here: the + module will be called rz-mtu3-cnt. + config STM32_TIMER_CNT tristate "STM32 Timer encoder counter driver" depends on MFD_STM32_TIMERS || COMPILE_TEST diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index b9a369e0d4fc..933fdd50b3e4 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -8,6 +8,7 @@ counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o +obj-$(CONFIG_RZ_MTU3_CNT) += rz-mtu3-cnt.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o obj-$(CONFIG_TI_EQEP) += ti-eqep.o diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c new file mode 100644 index 000000000000..0b0c5f9b2993 --- /dev/null +++ b/drivers/counter/rz-mtu3-cnt.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2L MTU3a Counter driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include <linux/clk.h> +#include <linux/counter.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> + +#include <clocksource/rz-mtu3.h> + +/* + * Register descriptions + * TSR: Timer Status Register + * TMDR1: Timer Mode Register 1 + * TMDR3: Timer Mode Register 3 + * TIOR: Timer I/O Control Register + * TCR: Timer Control Register + * TCNT: Timer Counter + * TGRA: Timer general register A + * TCNTLW: Timer Longword Counter + * TGRALW: Timer longword general register A + */ + +#define RZ_MTU3_TSR_TCFD BIT(7) /* Count Direction Flag */ + +#define RZ_MTU3_TMDR1_PH_CNT_MODE_1 (4) /* Phase counting mode 1 */ +#define RZ_MTU3_TMDR1_PH_CNT_MODE_2 (5) /* Phase counting mode 2 */ +#define RZ_MTU3_TMDR1_PH_CNT_MODE_3 (6) /* Phase counting mode 3 */ +#define RZ_MTU3_TMDR1_PH_CNT_MODE_4 (7) /* Phase counting mode 4 */ +#define RZ_MTU3_TMDR1_PH_CNT_MODE_5 (9) /* Phase counting mode 5 */ +#define RZ_MTU3_TMDR1_PH_CNT_MODE_MASK (0xf) + +/* + * LWA: MTU1/MTU2 Combination Longword Access Control + * 0: 16-bit, 1: 32-bit + */ +#define RZ_MTU3_TMDR3_LWA (0) + +/* + * PHCKSEL: External Input Phase Clock Select + * 0: MTCLKA and MTCLKB, 1: MTCLKC and MTCLKD + */ +#define RZ_MTU3_TMDR3_PHCKSEL (1) + +#define RZ_MTU3_16_BIT_MTU1_CH (0) +#define RZ_MTU3_16_BIT_MTU2_CH (1) +#define RZ_MTU3_32_BIT_CH (2) + +#define RZ_MTU3_TIOR_NO_OUTPUT (0) /* Output prohibited */ +#define RZ_MTU3_TIOR_IC_BOTH (10) /* Input capture at both edges */ + +#define SIGNAL_A_ID (0) +#define SIGNAL_B_ID (1) +#define SIGNAL_C_ID (2) +#define SIGNAL_D_ID (3) + +#define RZ_MTU3_MAX_HW_CNTR_CHANNELS (2) + +/** + * struct rz_mtu3_cnt - MTU3 counter private data + * + * @clk: MTU3 module clock + * @lock: Lock to prevent concurrent access for ceiling and count + * @ch: HW channels for the counters + * @mtu_16bit_max: Cache for 16-bit counters + * @mtu_32bit_max: Cache for 32-bit counters + */ +struct rz_mtu3_cnt { + struct clk *clk; + struct mutex lock; + struct rz_mtu3_channel *ch; + union { + u16 mtu_16bit_max[RZ_MTU3_MAX_HW_CNTR_CHANNELS]; + u32 mtu_32bit_max; + }; +}; + +static const enum counter_function rz_mtu3_count_functions[] = { + COUNTER_FUNCTION_QUADRATURE_X4, + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X2_B, +}; + +static inline size_t rz_mtu3_get_hw_ch(const size_t id) +{ + return (id == RZ_MTU3_32_BIT_CH) ? 0 : id; +} + +static inline struct rz_mtu3_channel * +rz_mtu3_get_ch(struct counter_device *counter, int id) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + const size_t ch_id = rz_mtu3_get_hw_ch(id); + + return &priv->ch[ch_id]; +} + +static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + unsigned long tmdr; + + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); + if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) + return false; + + if (id != RZ_MTU3_32_BIT_CH && !test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) + return false; + + return true; +} + +static int rz_mtu3_count_read(struct counter_device *counter, + struct counter_count *count, u64 *val) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + + if (rz_mtu3_is_counter_invalid(counter, count->id)) + return -EINVAL; + + if (count->id == RZ_MTU3_32_BIT_CH) + *val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW); + else + *val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT); + + return 0; +} + +static int rz_mtu3_count_write(struct counter_device *counter, + struct counter_count *count, const u64 val) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + struct rz_mtu3_cnt *const priv = counter_priv(counter); + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); + u32 ceiling; + + if (rz_mtu3_is_counter_invalid(counter, count->id)) + return -EINVAL; + + mutex_lock(&priv->lock); + if (count->id == RZ_MTU3_32_BIT_CH) + ceiling = priv->mtu_32bit_max; + else + ceiling = priv->mtu_16bit_max[ch_id]; + + if (count->id == RZ_MTU3_32_BIT_CH) + rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val); + else + rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val); + + mutex_unlock(&priv->lock); + + return 0; +} + +static int rz_mtu3_count_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + u8 timer_mode; + + timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); + + switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { + case RZ_MTU3_TMDR1_PH_CNT_MODE_1: + *function = COUNTER_FUNCTION_QUADRATURE_X4; + break; + case RZ_MTU3_TMDR1_PH_CNT_MODE_2: + *function = COUNTER_FUNCTION_PULSE_DIRECTION; + break; + case RZ_MTU3_TMDR1_PH_CNT_MODE_4: + *function = COUNTER_FUNCTION_QUADRATURE_X2_B; + break; + default: + /* + * TODO: + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3 + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5 + */ + return -EINVAL; + } + + return 0; +} + +static int rz_mtu3_count_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + u8 timer_mode; + + switch (function) { + case COUNTER_FUNCTION_QUADRATURE_X4: + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_1; + break; + case COUNTER_FUNCTION_PULSE_DIRECTION: + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_2; + break; + case COUNTER_FUNCTION_QUADRATURE_X2_B: + timer_mode = RZ_MTU3_TMDR1_PH_CNT_MODE_4; + break; + default: + /* + * TODO: + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3 + * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5 + */ + return -EINVAL; + } + + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); + + return 0; +} + +static int rz_mtu3_count_direction_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_direction *direction) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + u8 tsr; + + tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); + *direction = (tsr & RZ_MTU3_TSR_TCFD) ? + COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; + + return 0; +} + +static int rz_mtu3_count_ceiling_read(struct counter_device *counter, + struct counter_count *count, + u64 *ceiling) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); + + if (rz_mtu3_is_counter_invalid(counter, count->id)) + return -EINVAL; + + switch (count->id) { + case RZ_MTU3_16_BIT_MTU1_CH: + case RZ_MTU3_16_BIT_MTU2_CH: + *ceiling = priv->mtu_16bit_max[ch_id]; + break; + case RZ_MTU3_32_BIT_CH: + *ceiling = priv->mtu_32bit_max; + break; + default: + /* should never reach this path */ + return -EINVAL; + } + + if (count->id == RZ_MTU3_32_BIT_CH) + *ceiling = priv->mtu_32bit_max; + else + *ceiling = priv->mtu_16bit_max[ch_id]; + + return 0; +} + +static int rz_mtu3_count_ceiling_write(struct counter_device *counter, + struct counter_count *count, + u64 ceiling) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + struct rz_mtu3_cnt *const priv = counter_priv(counter); + const size_t ch_id = rz_mtu3_get_hw_ch(count->id); + + if (rz_mtu3_is_counter_invalid(counter, count->id)) + return -EINVAL; + + switch (count->id) { + case RZ_MTU3_16_BIT_MTU1_CH: + case RZ_MTU3_16_BIT_MTU2_CH: + if (ceiling > U16_MAX) + return -ERANGE; + priv->mtu_16bit_max[ch_id] = ceiling; + break; + case RZ_MTU3_32_BIT_CH: + if (ceiling > U32_MAX) + return -ERANGE; + priv->mtu_32bit_max = ceiling; + break; + default: + /* should never reach this path */ + return -EINVAL; + } + + mutex_lock(&priv->lock); + if (count->id == RZ_MTU3_32_BIT_CH) + rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling); + else + rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling); + + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); + mutex_unlock(&priv->lock); + + return 0; +} + +static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter, int id) +{ + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); + + /* Phase counting mode 1 is used as default in initialization. */ + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1); + + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); + rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TIOR, RZ_MTU3_TIOR_IC_BOTH); + + rz_mtu3_enable(ch1); + rz_mtu3_enable(ch2); +} + +static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); + + /* Phase counting mode 1 is used as default in initialization. */ + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1); + + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); + rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TIOR, RZ_MTU3_TIOR_NO_OUTPUT); + rz_mtu3_enable(ch); +} + +static int rz_mtu3_initialize_counter(struct counter_device *counter, int id) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); + + switch (id) { + case RZ_MTU3_16_BIT_MTU1_CH: + case RZ_MTU3_16_BIT_MTU2_CH: + mutex_lock(&ch->lock); + if (ch->function == RZ_MTU3_NORMAL) + ch->function = RZ_MTU3_16BIT_PHASE_COUNTING; + mutex_unlock(&ch->lock); + + if (ch->function != RZ_MTU3_16BIT_PHASE_COUNTING) + return -EBUSY; + + rz_mtu3_16bit_cnt_setting(counter, id); + + break; + case RZ_MTU3_32_BIT_CH: + /* + * 32-bit phase counting need MTU1 and MTU2 to create 32-bit + * cascade counter. + */ + mutex_lock(&ch1->lock); + if (ch1->function == RZ_MTU3_NORMAL) + ch1->function = RZ_MTU3_32BIT_PHASE_COUNTING; + mutex_unlock(&ch1->lock); + + mutex_lock(&ch2->lock); + if (ch2->function == RZ_MTU3_NORMAL) + ch2->function = RZ_MTU3_32BIT_PHASE_COUNTING; + mutex_unlock(&ch2->lock); + + if (ch1->function != RZ_MTU3_32BIT_PHASE_COUNTING || + ch2->function != RZ_MTU3_32BIT_PHASE_COUNTING) + return -EBUSY; + + rz_mtu3_32bit_cnt_setting(counter, id); + break; + default: + /* should never reach this path */ + return -EINVAL; + } + + return 0; +} + +static void rz_mtu3_terminate_counter(struct counter_device *counter, int id) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id); + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); + + if (id == RZ_MTU3_32_BIT_CH) { + mutex_lock(&ch1->lock); + ch1->function = RZ_MTU3_NORMAL; + mutex_unlock(&ch1->lock); + + mutex_lock(&ch2->lock); + ch2->function = RZ_MTU3_NORMAL; + mutex_unlock(&ch2->lock); + + rz_mtu3_disable(ch2); + rz_mtu3_disable(ch1); + } else { + mutex_lock(&ch->lock); + ch->function = RZ_MTU3_NORMAL; + mutex_unlock(&ch->lock); + + rz_mtu3_disable(ch); + } +} + +static int rz_mtu3_count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0); + struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1); + struct rz_mtu3_cnt *const priv = counter_priv(counter); + + if (count->id == RZ_MTU3_32_BIT_CH) { + mutex_lock(&priv->lock); + *enable = rz_mtu3_is_enabled(ch1) && + rz_mtu3_is_enabled(ch2); + mutex_unlock(&priv->lock); + } else { + *enable = rz_mtu3_is_enabled(ch); + } + + return 0; +} + +static int rz_mtu3_count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) +{ + struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); + struct rz_mtu3_cnt *const priv = counter_priv(counter); + int ret = 0; + + if (enable) { + pm_runtime_get_sync(ch->dev); + mutex_lock(&priv->lock); + ret = rz_mtu3_initialize_counter(counter, count->id); + mutex_unlock(&priv->lock); + } else { + mutex_lock(&priv->lock); + rz_mtu3_terminate_counter(counter, count->id); + mutex_unlock(&priv->lock); + pm_runtime_put(ch->dev); + } + + return ret; +} + +static int rz_mtu3_cascade_enable_get(struct counter_device *counter, + u8 *cascade_enable) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + unsigned long tmdr; + + pm_runtime_get_sync(priv->ch->dev); + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); + *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); + pm_runtime_put(priv->ch->dev); + + return 0; +} + +static int rz_mtu3_cascade_enable_set(struct counter_device *counter, + u8 cascade_enable) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + + pm_runtime_get_sync(priv->ch->dev); + rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, + RZ_MTU3_TMDR3_LWA, cascade_enable); + pm_runtime_put(priv->ch->dev); + + return 0; +} + +static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *counter, + u32 *ext_input_phase_clock_select) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + unsigned long tmdr; + + pm_runtime_get_sync(priv->ch->dev); + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); + *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); + pm_runtime_put(priv->ch->dev); + + return 0; +} + +static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *counter, + u32 ext_input_phase_clock_select) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + + pm_runtime_get_sync(priv->ch->dev); + rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, + RZ_MTU3_TMDR3_PHCKSEL, + ext_input_phase_clock_select); + pm_runtime_put(priv->ch->dev); + + return 0; +} + +static struct counter_comp rz_mtu3_count_ext[] = { + COUNTER_COMP_DIRECTION(rz_mtu3_count_direction_read), + COUNTER_COMP_ENABLE(rz_mtu3_count_enable_read, + rz_mtu3_count_enable_write), + COUNTER_COMP_CEILING(rz_mtu3_count_ceiling_read, + rz_mtu3_count_ceiling_write), +}; + +static const enum counter_synapse_action rz_mtu3_synapse_actions[] = { + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_NONE, +}; + +static int rz_mtu3_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) +{ + struct rz_mtu3_cnt *const priv = counter_priv(counter); + enum counter_function function; + bool mtclkc_mtclkd; + unsigned long tmdr; + int err; + + err = rz_mtu3_count_function_read(counter, count, &function); + if (err) + return err; + + /* Default action mode */ + *action = COUNTER_SYNAPSE_ACTION_NONE; + + if (count->id != RZ_MTU3_16_BIT_MTU1_CH) { + tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); + mtclkc_mtclkd = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); + if (mtclkc_mtclkd && (synapse->signal->id == SIGNAL_A_ID || + synapse->signal->id == SIGNAL_B_ID)) + return -EINVAL; + + if (!mtclkc_mtclkd && (synapse->signal->id == SIGNAL_C_ID || + synapse->signal->id == SIGNAL_D_ID)) + return -EINVAL; + } + + switch (function) { + case COUNTER_FUNCTION_PULSE_DIRECTION: + /* + * Rising edges on signal A (signal C) updates the respective + * count. The input level of signal B (signal D) determines + * direction. + */ + if (synapse->signal->id == SIGNAL_A_ID || + synapse->signal->id == SIGNAL_C_ID) + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; + break; + case COUNTER_FUNCTION_QUADRATURE_X2_B: + /* + * Any state transition on quadrature pair signal B (signal D) + * updates the respective count. + */ + if (synapse->signal->id == SIGNAL_B_ID || + synapse->signal->id == SIGNAL_D_ID) + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + break; + case COUNTER_FUNCTION_QUADRATURE_X4: + /* counts up/down on both edges of A (C) and B (D) signal */ + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + break; + default: + /* should never reach this path */ + return -EINVAL; + } + + return 0; +} + +static const struct counter_ops rz_mtu3_cnt_ops = { + .count_read = rz_mtu3_count_read, + .count_write = rz_mtu3_count_write, + .function_read = rz_mtu3_count_function_read, + .function_write = rz_mtu3_count_function_write, + .action_read = rz_mtu3_action_read, +}; + +#define RZ_MTU3_PHASE_SIGNAL(_id, _name) { \ + .id = (_id), \ + .name = (_name), \ +} + +static struct counter_signal rz_mtu3_signals[] = { + RZ_MTU3_PHASE_SIGNAL(SIGNAL_A_ID, "MTU1 MTCLKA"), + RZ_MTU3_PHASE_SIGNAL(SIGNAL_B_ID, "MTU1 MTCLKB"), + RZ_MTU3_PHASE_SIGNAL(SIGNAL_C_ID, "MTU2 MTCLKC"), + RZ_MTU3_PHASE_SIGNAL(SIGNAL_D_ID, "MTU2 MTCLKD"), +}; + +static struct counter_synapse rz_mtu3_mtu1_count_synapses[] = { + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals, + }, + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals + 1, + } +}; + +static struct counter_synapse rz_mtu3_mtu2_count_synapses[] = { + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals, + }, + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals + 1, + }, + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals + 2, + }, + { + .actions_list = rz_mtu3_synapse_actions, + .num_actions = ARRAY_SIZE(rz_mtu3_synapse_actions), + .signal = rz_mtu3_signals + 3, + } +}; + +static struct counter_count rz_mtu3_counts[] = { + { + .id = RZ_MTU3_16_BIT_MTU1_CH, + .name = "Channel 1 Count", + .functions_list = rz_mtu3_count_functions, + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), + .synapses = rz_mtu3_mtu1_count_synapses, + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu1_count_synapses), + .ext = rz_mtu3_count_ext, + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), + }, + { + .id = RZ_MTU3_16_BIT_MTU2_CH, + .name = "Channel 2 Count", + .functions_list = rz_mtu3_count_functions, + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), + .synapses = rz_mtu3_mtu2_count_synapses, + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu2_count_synapses), + .ext = rz_mtu3_count_ext, + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), + }, + { + .id = RZ_MTU3_32_BIT_CH, + .name = "Channel 1 and 2 (combined) Count", + .functions_list = rz_mtu3_count_functions, + .num_functions = ARRAY_SIZE(rz_mtu3_count_functions), + .synapses = rz_mtu3_mtu2_count_synapses, + .num_synapses = ARRAY_SIZE(rz_mtu3_mtu2_count_synapses), + .ext = rz_mtu3_count_ext, + .num_ext = ARRAY_SIZE(rz_mtu3_count_ext), + } +}; + +static const char *const rz_mtu3_ext_input_phase_clock_select[] = { + "MTCLKA-MTCLKB", + "MTCLKC-MTCLKD", +}; + +static DEFINE_COUNTER_ENUM(rz_mtu3_ext_input_phase_clock_select_enum, + rz_mtu3_ext_input_phase_clock_select); + +static struct counter_comp rz_mtu3_device_ext[] = { + COUNTER_COMP_DEVICE_BOOL("cascade_enable", + rz_mtu3_cascade_enable_get, + rz_mtu3_cascade_enable_set), + COUNTER_COMP_DEVICE_ENUM("external_input_phase_clock_select", + rz_mtu3_ext_input_phase_clock_select_get, + rz_mtu3_ext_input_phase_clock_select_set, + rz_mtu3_ext_input_phase_clock_select_enum), +}; + +static int rz_mtu3_cnt_pm_runtime_suspend(struct device *dev) +{ + struct clk *const clk = dev_get_drvdata(dev); + + clk_disable_unprepare(clk); + + return 0; +} + +static int rz_mtu3_cnt_pm_runtime_resume(struct device *dev) +{ + struct clk *const clk = dev_get_drvdata(dev); + + clk_prepare_enable(clk); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(rz_mtu3_cnt_pm_ops, + rz_mtu3_cnt_pm_runtime_suspend, + rz_mtu3_cnt_pm_runtime_resume, NULL); + +static void rz_mtu3_cnt_pm_disable(void *data) +{ + struct device *dev = data; + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); +} + +static int rz_mtu3_cnt_probe(struct platform_device *pdev) +{ + struct rz_mtu3 *ddata = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct counter_device *counter; + struct rz_mtu3_channel *ch; + struct rz_mtu3_cnt *priv; + unsigned int i; + int ret; + + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) + return -ENOMEM; + + priv = counter_priv(counter); + priv->clk = ddata->clk; + priv->mtu_32bit_max = U32_MAX; + priv->ch = &ddata->channels[RZ_MTU1]; + ch = &priv->ch[0]; + for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++) { + ch->dev = dev; + priv->mtu_16bit_max[i] = U16_MAX; + ch++; + } + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv->clk); + clk_prepare_enable(priv->clk); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = devm_add_action_or_reset(&pdev->dev, rz_mtu3_cnt_pm_disable, dev); + if (ret < 0) + goto disable_clock; + + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &rz_mtu3_cnt_ops; + counter->counts = rz_mtu3_counts; + counter->num_counts = ARRAY_SIZE(rz_mtu3_counts); + counter->signals = rz_mtu3_signals; + counter->num_signals = ARRAY_SIZE(rz_mtu3_signals); + counter->ext = rz_mtu3_device_ext; + counter->num_ext = ARRAY_SIZE(rz_mtu3_device_ext); + + /* Register Counter device */ + ret = devm_counter_add(dev, counter); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to add counter\n"); + goto disable_clock; + } + + return 0; + +disable_clock: + clk_disable_unprepare(priv->clk); + + return ret; +} + +static struct platform_driver rz_mtu3_cnt_driver = { + .probe = rz_mtu3_cnt_probe, + .driver = { + .name = "rz-mtu3-counter", + .pm = pm_ptr(&rz_mtu3_cnt_pm_ops), + }, +}; +module_platform_driver(rz_mtu3_cnt_driver); + +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); +MODULE_ALIAS("platform:rz-mtu3-counter"); +MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a counter driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(COUNTER);
Add RZ/G2L MTU3a counter driver. This IP supports the following phase counting modes on MTU1 and MTU2 channels 1) 16-bit phase counting modes on MTU1 and MTU2 channels. 2) 32-bit phase counting mode by cascading MTU1 and MTU2 channels. This patch adds 3 counter value channels. count0: 16-bit phase counter value channel on MTU1 count1: 16-bit phase counter value channel on MTU2 count2: 32-bit phase counter value channel by cascading MTU1 and MTU2 channels. The external input phase clock pin for the counter value channels are as follows: count0: "MTCLKA-MTCLKB" count1: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" count2: "MTCLKA-MTCLKB" or "MTCLKC-MTCLKD" Use the sysfs variable "external_input_phase_clock_select" to select the external input phase clock pin and "cascade_enable" to enable/disable cascading of channels. Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> --- v6->v7: * Updated commit description * Added Register descriptions * Opimized size of cache variable by using union * Used test_bit() in rz_mtu3_is_counter_invalid() * Replaced val->timer_mode in rz_mtu3_count_function_{read,write} * Added TODO comment phase3 and phase5 modes. * replaced if-else with ternary expression in rz_mtu3_count_direction_read() * Used switch statement in rz_mtu3_count_ceiling_read to consistent with write * Provided default case for all switch statement. * Add mutex lock for avoiding races with other devices * Updated comments in rz_mtu3_action_read * Replaced COUNTER_COMP_DEVICE_BOOL->COUNTER_COMP_DEVICE_BOOL for cascade_enable * Replaced RZ_MTU3_GET_HW_CH->rz_mtu3_get_hw_ch * Added rz_mtu3_get_ch() to get channels * used rz_mtu3_shared_reg_update_bit for cascade_enable and selecting phase input clock. * Added rz_mtu3_is_counter_invalid() check in rz_mtu3_count_ceiling_read() v5->v6: * Updated KConfig and commit description * Sorted header * Fixed RZ_MTU3_GET_HW_CH Macro for argument reuse 'id' - possible side-effects? * Replaced SET_RUNTIME_PM_OPS->DEFINE_RUNTIME_DEV_PM_OPS and removed __maybe_unused from suspend/resume() v4->v5: * Updated the Kconfig with SoC vendor name * Introduced rz_mtu3_is_counter_invalid() * replaced pointer to an array of struct rz_mtu3_channel with a simple pointer to struct rz_mtu3_channel. * Added long_word_access_ctrl_mode sysfs entry for 16-bit and 32-bit access * Added external_input_phase_clock_select sysfs entry for selecting input clocks. * used preprocessor defines represent SIGNAL_{A,B,C,D}_ID instead of signal ids. v3->v4: * There is no resource associated with "rz-mtu3-counter" compatible and moved the code to mfd subsystem as it binds against "rz-mtu". * Removed struct platform_driver rz_mtu3_cnt_driver. * Updated commit description * Updated Kconfig description * Added macros RZ_MTU3_16_BIT_MTU{1,2}_CH for MTU1 and MTU2 channels * Added RZ_MTU3_GET_HW_CH macro for getting channel ID. * replaced priv->ch[id]->priv->ch[0] in rz_mtu3_count_read() * Cached counter max values * replaced cnt->tsr in rz_mtu3_count_direction_read() * Added comments for RZ_MTU3_TCR_CCLR_NONE * Replaced if with switch in rz_mtu3_initialize_counter() and rz_mtu3_count_ceiling_write() * Added locks in initialize, terminate and enable_read to prevent races. * Updated rz_mtu3_action_read to take care of MTU2 signals. * Added separate distinct array for each group of Synapse. * Moved pm handling to parent. v1->v3: * Modelled as a counter device supporting 3 counters(2 16-bit and 32-bit) * Add kernel-doc comments to document struct rz_mtu3_cnt * Removed mmio variable from struct rz_mtu3_cnt * Removed cnt local variable from rz_mtu3_count_read() * Replaced -EINVAL->-ERANGE for out of range error conditions. * Removed explicit cast from write functions. * Removed local variable val from rz_mtu3_count_ceiling_read() * Added lock for RMW for counter/ceiling updates. * Added different synapses for counter0 and counter{1,2} * Used ARRAY for assigning num_counts. * Added PM runtime for managing clocks. * Add MODULE_IMPORT_NS(COUNTER) to import the COUNTER namespace. --- drivers/counter/Kconfig | 11 + drivers/counter/Makefile | 1 + drivers/counter/rz-mtu3-cnt.c | 793 ++++++++++++++++++++++++++++++++++ 3 files changed, 805 insertions(+) create mode 100644 drivers/counter/rz-mtu3-cnt.c