Re: [PATCH v7 4/5] counter: Add Renesas RZ/G2L MTU3a counter driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxx>

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@xxxxxxxxxxxxxx>");
> +MODULE_ALIAS("platform:rz-mtu3-counter");
> +MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a counter driver");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS(COUNTER);
> -- 
> 2.25.1
> 

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux