root/drivers/counter/rz-mtu3-cnt.c
// 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(counter->parent);
        tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
        pm_runtime_put(counter->parent);

        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(counter->parent);
        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(counter->parent);
        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(counter->parent);
        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(counter->parent);
        mutex_unlock(&priv->lock);

        return 0;
}

static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch,
                                              struct counter_device *const counter,
                                              enum counter_function *function)
{
        u8 timer_mode;

        pm_runtime_get_sync(counter->parent);
        timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1);
        pm_runtime_put(counter->parent);

        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, counter, 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(counter->parent);
        rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode);
        pm_runtime_put(counter->parent);
        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(counter->parent);
        tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR);
        pm_runtime_put(counter->parent);

        *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(counter->parent);
        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(counter->parent);
        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_cnt *const priv = counter_priv(counter);
        int ret = 0;

        mutex_lock(&priv->lock);

        if (priv->count_is_enabled[count->id] == enable)
                goto exit;

        if (enable) {
                pm_runtime_get_sync(counter->parent);
                ret = rz_mtu3_initialize_counter(counter, count->id);
                if (ret == 0)
                        priv->count_is_enabled[count->id] = true;
        } else {
                rz_mtu3_terminate_counter(counter, count->id);
                priv->count_is_enabled[count->id] = false;
                pm_runtime_put(counter->parent);
        }

exit:
        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(counter->parent);
        tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
        pm_runtime_put(counter->parent);
        *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(counter->parent);
        rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
                                      RZ_MTU3_TMDR3_LWA, cascade_enable);
        pm_runtime_put(counter->parent);
        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(counter->parent);
        tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
        pm_runtime_put(counter->parent);
        *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(counter->parent);
        rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
                                      RZ_MTU3_TMDR3_PHCKSEL,
                                      ext_input_phase_clock_select);
        pm_runtime_put(counter->parent);
        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, counter, &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");