| // 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/mfd/rz-mtu3.h> | |
| #include <linux/module.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/pm_runtime.h> | |
| #include <linux/types.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) | |
| #define RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS (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 | |
| * @count_is_enabled: Enabled state of Counter value channel | |
| * @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; | |
| bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS]; | |
| 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; | |
| pm_runtime_get_sync(priv->ch->dev); | |
| tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); | |
| pm_runtime_put(priv->ch->dev); | |
| 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_lock_if_counter_is_valid(struct counter_device *counter, | |
| struct rz_mtu3_channel *const ch, | |
| struct rz_mtu3_cnt *const priv, | |
| int id) | |
| { | |
| mutex_lock(&priv->lock); | |
| if (ch->is_busy && !priv->count_is_enabled[id]) { | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| if (rz_mtu3_is_counter_invalid(counter, id)) { | |
| mutex_unlock(&priv->lock); | |
| return -EBUSY; | |
| } | |
| return 0; | |
| } | |
| static int rz_mtu3_lock_if_count_is_enabled(struct rz_mtu3_channel *const ch, | |
| struct rz_mtu3_cnt *const priv, | |
| int id) | |
| { | |
| mutex_lock(&priv->lock); | |
| if (ch->is_busy && !priv->count_is_enabled[id]) { | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| 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); | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| int ret; | |
| ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| pm_runtime_get_sync(ch->dev); | |
| 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); | |
| pm_runtime_put(ch->dev); | |
| mutex_unlock(&priv->lock); | |
| 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); | |
| int ret; | |
| ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| pm_runtime_get_sync(ch->dev); | |
| 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); | |
| pm_runtime_put(ch->dev); | |
| mutex_unlock(&priv->lock); | |
| return 0; | |
| } | |
| static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch, | |
| struct rz_mtu3_cnt *const priv, | |
| enum counter_function *function) | |
| { | |
| u8 timer_mode; | |
| pm_runtime_get_sync(ch->dev); | |
| timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); | |
| pm_runtime_put(ch->dev); | |
| switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { | |
| case RZ_MTU3_TMDR1_PH_CNT_MODE_1: | |
| *function = COUNTER_FUNCTION_QUADRATURE_X4; | |
| return 0; | |
| case RZ_MTU3_TMDR1_PH_CNT_MODE_2: | |
| *function = COUNTER_FUNCTION_PULSE_DIRECTION; | |
| return 0; | |
| case RZ_MTU3_TMDR1_PH_CNT_MODE_4: | |
| *function = COUNTER_FUNCTION_QUADRATURE_X2_B; | |
| return 0; | |
| default: | |
| /* | |
| * TODO: | |
| * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3 | |
| * - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5 | |
| */ | |
| return -EINVAL; | |
| } | |
| } | |
| 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); | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| int ret; | |
| ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| ret = rz_mtu3_count_function_read_helper(ch, priv, function); | |
| mutex_unlock(&priv->lock); | |
| return ret; | |
| } | |
| 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); | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| u8 timer_mode; | |
| int ret; | |
| ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| 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 | |
| */ | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| pm_runtime_get_sync(ch->dev); | |
| rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); | |
| pm_runtime_put(ch->dev); | |
| mutex_unlock(&priv->lock); | |
| 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); | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| int ret; | |
| u8 tsr; | |
| ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| pm_runtime_get_sync(ch->dev); | |
| tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); | |
| pm_runtime_put(ch->dev); | |
| *direction = (tsr & RZ_MTU3_TSR_TCFD) ? | |
| COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; | |
| mutex_unlock(&priv->lock); | |
| return 0; | |
| } | |
| static int rz_mtu3_count_ceiling_read(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); | |
| int ret; | |
| ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| 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 */ | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| mutex_unlock(&priv->lock); | |
| 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); | |
| int ret; | |
| ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| switch (count->id) { | |
| case RZ_MTU3_16_BIT_MTU1_CH: | |
| case RZ_MTU3_16_BIT_MTU2_CH: | |
| if (ceiling > U16_MAX) { | |
| mutex_unlock(&priv->lock); | |
| return -ERANGE; | |
| } | |
| priv->mtu_16bit_max[ch_id] = ceiling; | |
| break; | |
| case RZ_MTU3_32_BIT_CH: | |
| if (ceiling > U32_MAX) { | |
| mutex_unlock(&priv->lock); | |
| return -ERANGE; | |
| } | |
| priv->mtu_32bit_max = ceiling; | |
| break; | |
| default: | |
| /* should never reach this path */ | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| pm_runtime_get_sync(ch->dev); | |
| 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); | |
| pm_runtime_put(ch->dev); | |
| mutex_unlock(&priv->lock); | |
| return 0; | |
| } | |
| static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter) | |
| { | |
| 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: | |
| if (!rz_mtu3_request_channel(ch)) | |
| return -EBUSY; | |
| rz_mtu3_16bit_cnt_setting(counter, id); | |
| return 0; | |
| case RZ_MTU3_32_BIT_CH: | |
| /* | |
| * 32-bit phase counting need MTU1 and MTU2 to create 32-bit | |
| * cascade counter. | |
| */ | |
| if (!rz_mtu3_request_channel(ch1)) | |
| return -EBUSY; | |
| if (!rz_mtu3_request_channel(ch2)) { | |
| rz_mtu3_release_channel(ch1); | |
| return -EBUSY; | |
| } | |
| rz_mtu3_32bit_cnt_setting(counter); | |
| return 0; | |
| default: | |
| /* should never reach this path */ | |
| return -EINVAL; | |
| } | |
| } | |
| 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) { | |
| rz_mtu3_release_channel(ch2); | |
| rz_mtu3_release_channel(ch1); | |
| rz_mtu3_disable(ch2); | |
| rz_mtu3_disable(ch1); | |
| } else { | |
| rz_mtu3_release_channel(ch); | |
| 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); | |
| int ret; | |
| ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| if (count->id == RZ_MTU3_32_BIT_CH) | |
| *enable = rz_mtu3_is_enabled(ch1) && rz_mtu3_is_enabled(ch2); | |
| else | |
| *enable = rz_mtu3_is_enabled(ch); | |
| mutex_unlock(&priv->lock); | |
| 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) { | |
| mutex_lock(&priv->lock); | |
| pm_runtime_get_sync(ch->dev); | |
| ret = rz_mtu3_initialize_counter(counter, count->id); | |
| if (ret == 0) | |
| priv->count_is_enabled[count->id] = true; | |
| mutex_unlock(&priv->lock); | |
| } else { | |
| mutex_lock(&priv->lock); | |
| rz_mtu3_terminate_counter(counter, count->id); | |
| priv->count_is_enabled[count->id] = false; | |
| pm_runtime_put(ch->dev); | |
| mutex_unlock(&priv->lock); | |
| } | |
| return ret; | |
| } | |
| static int rz_mtu3_lock_if_ch0_is_enabled(struct rz_mtu3_cnt *const priv) | |
| { | |
| mutex_lock(&priv->lock); | |
| if (priv->ch->is_busy && !(priv->count_is_enabled[RZ_MTU3_16_BIT_MTU1_CH] || | |
| priv->count_is_enabled[RZ_MTU3_32_BIT_CH])) { | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter, | |
| u8 *cascade_enable) | |
| { | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| unsigned long tmdr; | |
| int ret; | |
| ret = rz_mtu3_lock_if_ch0_is_enabled(priv); | |
| if (ret) | |
| return ret; | |
| pm_runtime_get_sync(priv->ch->dev); | |
| tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); | |
| pm_runtime_put(priv->ch->dev); | |
| *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); | |
| mutex_unlock(&priv->lock); | |
| return 0; | |
| } | |
| static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter, | |
| u8 cascade_enable) | |
| { | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| int ret; | |
| ret = rz_mtu3_lock_if_ch0_is_enabled(priv); | |
| if (ret) | |
| return ret; | |
| 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); | |
| mutex_unlock(&priv->lock); | |
| 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; | |
| int ret; | |
| ret = rz_mtu3_lock_if_ch0_is_enabled(priv); | |
| if (ret) | |
| return ret; | |
| pm_runtime_get_sync(priv->ch->dev); | |
| tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); | |
| pm_runtime_put(priv->ch->dev); | |
| *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); | |
| mutex_unlock(&priv->lock); | |
| 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); | |
| int ret; | |
| ret = rz_mtu3_lock_if_ch0_is_enabled(priv); | |
| if (ret) | |
| return ret; | |
| 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); | |
| mutex_unlock(&priv->lock); | |
| 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) | |
| { | |
| const bool is_signal_ab = (synapse->signal->id == SIGNAL_A_ID) || | |
| (synapse->signal->id == SIGNAL_B_ID); | |
| struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); | |
| struct rz_mtu3_cnt *const priv = counter_priv(counter); | |
| enum counter_function function; | |
| bool mtclkc_mtclkd; | |
| unsigned long tmdr; | |
| int ret; | |
| ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id); | |
| if (ret) | |
| return ret; | |
| ret = rz_mtu3_count_function_read_helper(ch, priv, &function); | |
| if (ret) { | |
| mutex_unlock(&priv->lock); | |
| return ret; | |
| } | |
| /* 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 && is_signal_ab) || | |
| (!mtclkc_mtclkd && !is_signal_ab)) { | |
| mutex_unlock(&priv->lock); | |
| return 0; | |
| } | |
| } | |
| 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 */ | |
| mutex_unlock(&priv->lock); | |
| return -EINVAL; | |
| } | |
| mutex_unlock(&priv->lock); | |
| 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 (cascaded) 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_counts_enable", | |
| rz_mtu3_cascade_counts_enable_get, | |
| rz_mtu3_cascade_counts_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_MTU3_CHAN_1]; | |
| 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); |