@@ -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 MTU3 counter driver"
+ depends on RZ_MTU3 || COMPILE_TEST
+ help
+ Enable support for MTU3 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
@@ -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
new file mode 100644
@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G2L MTU3a Counter driver
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ */
+
+#include <clocksource/rz-mtu3.h>
+#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>
+
+#define RZ_MTU3_TSR_TCFD BIT(7)
+#define RZ_MTU3_MAX_HW_CNTR_CHANNELS (2)
+
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_1 (4)
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_2 (5)
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_3 (6)
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_4 (7)
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_5 (9)
+#define RZ_MTU3_TMDR1_PH_CNT_MODE_MASK (0xf)
+
+#define RZ_MTU3_TMDR3_LWA BIT(0)
+#define RZ_MTU3_TMDR3_PHCKSEL BIT(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)
+#define RZ_MTU3_TIOR_IC_BOTH (10)
+
+#define RZ_MTU3_GET_HW_CH(id) (((id) == RZ_MTU3_32_BIT_CH) ? 0 : (id))
+
+#define SIGNAL_A_ID (0)
+#define SIGNAL_B_ID (1)
+#define SIGNAL_C_ID (2)
+#define SIGNAL_D_ID (3)
+
+/**
+ * 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;
+ 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 bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ u16 val;
+
+ val = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3) & RZ_MTU3_TMDR3_LWA;
+ if (id == RZ_MTU3_32_BIT_CH && val)
+ return false;
+
+ if (id != RZ_MTU3_32_BIT_CH && !val)
+ return false;
+
+ return true;
+}
+
+static int rz_mtu3_count_read(struct counter_device *counter,
+ struct counter_count *count, u64 *val)
+{
+ 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;
+
+ if (count->id == RZ_MTU3_32_BIT_CH)
+ *val = rz_mtu3_32bit_ch_read(priv->ch + ch_id, RZ_MTU3_TCNTLW);
+ else
+ *val = rz_mtu3_16bit_ch_read(priv->ch + ch_id, 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_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(priv->ch + ch_id, RZ_MTU3_TCNTLW, val);
+ else
+ rz_mtu3_16bit_ch_write(priv->ch + ch_id, 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_cnt *const priv = counter_priv(counter);
+ const size_t ch_id = RZ_MTU3_GET_HW_CH(count->id);
+ u8 val;
+
+ val = rz_mtu3_8bit_ch_read(priv->ch + ch_id, RZ_MTU3_TMDR1);
+
+ switch (val & 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:
+ 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_cnt *const priv = counter_priv(counter);
+ const size_t ch_id = RZ_MTU3_GET_HW_CH(count->id);
+ u8 mode;
+
+ switch (function) {
+ case COUNTER_FUNCTION_QUADRATURE_X4:
+ mode = RZ_MTU3_TMDR1_PH_CNT_MODE_1;
+ break;
+ case COUNTER_FUNCTION_PULSE_DIRECTION:
+ mode = RZ_MTU3_TMDR1_PH_CNT_MODE_2;
+ break;
+ case COUNTER_FUNCTION_QUADRATURE_X2_B:
+ mode = RZ_MTU3_TMDR1_PH_CNT_MODE_4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rz_mtu3_8bit_ch_write(priv->ch + ch_id, RZ_MTU3_TMDR1, 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_cnt *const priv = counter_priv(counter);
+ const size_t ch_id = RZ_MTU3_GET_HW_CH(count->id);
+ u8 tsr;
+
+ tsr = rz_mtu3_8bit_ch_read(priv->ch + ch_id, RZ_MTU3_TSR);
+
+ if (tsr & RZ_MTU3_TSR_TCFD)
+ *direction = COUNTER_COUNT_DIRECTION_FORWARD;
+ else
+ *direction = 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 (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_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;
+ }
+
+ mutex_lock(&priv->lock);
+ if (count->id == RZ_MTU3_32_BIT_CH)
+ rz_mtu3_32bit_ch_write(priv->ch + ch_id, RZ_MTU3_TGRALW, ceiling);
+ else
+ rz_mtu3_16bit_ch_write(priv->ch + ch_id, RZ_MTU3_TGRA, ceiling);
+
+ rz_mtu3_8bit_ch_write(priv->ch + ch_id, 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_cnt *const priv = counter_priv(counter);
+ struct rz_mtu3_channel *ch1 = priv->ch;
+ struct rz_mtu3_channel *ch2 = ch1 + 1;
+
+ /*
+ * 32-bit phase counting need MTU1 and MTU2 to create 32-bit cascade
+ * counter.
+ */
+ ch1->function = RZ_MTU3_32BIT_PHASE_COUNTING;
+ ch2->function = RZ_MTU3_32BIT_PHASE_COUNTING;
+
+ /* 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_cnt *const priv = counter_priv(counter);
+ struct rz_mtu3_channel *ch = priv->ch + id;
+
+ ch->function = RZ_MTU3_16BIT_PHASE_COUNTING;
+
+ /* 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_enable(ch);
+}
+
+static int rz_mtu3_initialize_counter(struct counter_device *counter, int id)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ struct rz_mtu3_channel *ch1 = priv->ch;
+ struct rz_mtu3_channel *ch2 = ch1 + 1;
+
+ mutex_lock(&priv->lock);
+ rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TIOR, RZ_MTU3_TIOR_NO_OUTPUT);
+ switch (id) {
+ case RZ_MTU3_16_BIT_MTU1_CH:
+ case RZ_MTU3_16_BIT_MTU2_CH:
+ if ((priv->ch + id)->function != RZ_MTU3_NORMAL) {
+ mutex_unlock(&priv->lock);
+ return -EBUSY;
+ }
+
+ rz_mtu3_16bit_cnt_setting(counter, id);
+ break;
+ case RZ_MTU3_32_BIT_CH:
+ if (ch1->function != RZ_MTU3_NORMAL ||
+ ch2->function != RZ_MTU3_NORMAL) {
+ mutex_unlock(&priv->lock);
+ return -EBUSY;
+ }
+ rz_mtu3_32bit_cnt_setting(counter, id);
+ break;
+ }
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ struct rz_mtu3_channel *ch1 = priv->ch;
+ struct rz_mtu3_channel *ch2 = ch1 + 1;
+
+ mutex_lock(&priv->lock);
+ if (id == RZ_MTU3_32_BIT_CH) {
+ ch1->function = RZ_MTU3_NORMAL;
+ ch2->function = RZ_MTU3_NORMAL;
+ rz_mtu3_disable(ch2);
+ rz_mtu3_disable(ch1);
+ } else {
+ (priv->ch + id)->function = RZ_MTU3_NORMAL;
+ rz_mtu3_disable(priv->ch + id);
+ }
+ mutex_unlock(&priv->lock);
+}
+
+static int rz_mtu3_count_enable_read(struct counter_device *counter,
+ struct counter_count *count, u8 *enable)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ struct rz_mtu3_channel *ch1 = priv->ch;
+ struct rz_mtu3_channel *ch2 = ch1 + 1;
+
+ 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(priv->ch + count->id);
+ }
+
+ return 0;
+}
+
+static int rz_mtu3_count_enable_write(struct counter_device *counter,
+ struct counter_count *count, u8 enable)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ const size_t ch_id = RZ_MTU3_GET_HW_CH(count->id);
+ struct rz_mtu3_channel *ch = priv->ch + ch_id;
+ int ret = 0;
+
+ if (enable) {
+ pm_runtime_get_sync(ch->dev);
+ ret = rz_mtu3_initialize_counter(counter, count->id);
+ } else {
+ rz_mtu3_terminate_counter(counter, count->id);
+ pm_runtime_put(ch->dev);
+ }
+
+ return ret;
+}
+
+static int rz_mtu3_long_word_access_ctrl_mode_get(struct counter_device *counter,
+ u32 *lwa_ctrl_mode)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ u16 val;
+
+ pm_runtime_get_sync(priv->ch->dev);
+ val = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
+ *lwa_ctrl_mode = val & RZ_MTU3_TMDR3_LWA;
+ pm_runtime_put(priv->ch->dev);
+
+ return 0;
+}
+
+static int rz_mtu3_long_word_access_ctrl_mode_set(struct counter_device *counter,
+ u32 lwa_ctrl_mode)
+{
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ u16 val;
+
+ pm_runtime_get_sync(priv->ch->dev);
+ val = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
+ if (lwa_ctrl_mode)
+ val |= RZ_MTU3_TMDR3_LWA;
+ else
+ val &= ~RZ_MTU3_TMDR3_LWA;
+
+ rz_mtu3_shared_reg_write(priv->ch, RZ_MTU3_TMDR3, val);
+ 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);
+ u16 val;
+
+ pm_runtime_get_sync(priv->ch->dev);
+ val = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
+ *ext_input_phase_clock_select = (val & RZ_MTU3_TMDR3_PHCKSEL) >> 1;
+ 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);
+ u16 val;
+
+ pm_runtime_get_sync(priv->ch->dev);
+ val = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
+ if (ext_input_phase_clock_select)
+ val |= RZ_MTU3_TMDR3_PHCKSEL;
+ else
+ val &= ~RZ_MTU3_TMDR3_PHCKSEL;
+
+ rz_mtu3_shared_reg_write(priv->ch, RZ_MTU3_TMDR3, val);
+ 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)
+{
+ enum counter_function function;
+ int err;
+
+ err = rz_mtu3_count_function_read(counter, count, &function);
+ if (err)
+ return err;
+
+ /* Default action mode */
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+
+ switch (function) {
+ case COUNTER_FUNCTION_PULSE_DIRECTION:
+ /*
+ * Rising edges on signal A updates the respective count.
+ * The input level of signal B 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 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 and B signal*/
+ *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+ break;
+ default:
+ 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_long_word_access_ctrl_modes[] = {
+ "16-bit",
+ "32-bit",
+};
+
+static DEFINE_COUNTER_ENUM(rz_mtu3_long_word_access_ctrl_mode_enum,
+ rz_mtu3_long_word_access_ctrl_modes);
+
+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_ENUM("long_word_access_ctrl_mode",
+ rz_mtu3_long_word_access_ctrl_mode_get,
+ rz_mtu3_long_word_access_ctrl_mode_set,
+ rz_mtu3_long_word_access_ctrl_mode_enum),
+ 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 __maybe_unused 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 __maybe_unused rz_mtu3_cnt_pm_runtime_resume(struct device *dev)
+{
+ struct clk *const clk = dev_get_drvdata(dev);
+
+ clk_prepare_enable(clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rz_mtu3_cnt_pm_ops = {
+ SET_RUNTIME_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_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];
+ for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++) {
+ (priv->ch + i)->dev = dev;
+ priv->mtu_16bit_max[i] = U16_MAX;
+ }
+
+ 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 = &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 MTU3 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. This patch adds 3 counters by creating 3 logical channels counter0: 16-bit phase counter on MTU1 channel counter1: 16-bit phase counter on MTU2 channel counter2: 32-bit phase counter by cascading MTU1 and MTU2 channels. Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> --- 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 | 713 ++++++++++++++++++++++++++++++++++ 3 files changed, 725 insertions(+) create mode 100644 drivers/counter/rz-mtu3-cnt.c