root/drivers/clk/spacemit/ccu_mix.h
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2024 SpacemiT Technology Co. Ltd
 * Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
 */

#ifndef _CCU_MIX_H_
#define _CCU_MIX_H_

#include <linux/clk-provider.h>

#include "ccu_common.h"

/**
 * struct ccu_gate_config - Gate configuration
 *
 * @mask:       Mask to enable the gate. Some clocks may have more than one bit
 *              set in this field.
 * @inverted:   Enable bit is inverted, 1 - disable clock, 0 - enable clock
 */
struct ccu_gate_config {
        u32 mask;
        bool inverted;
};

struct ccu_factor_config {
        u32 div;
        u32 mul;
};

struct ccu_mux_config {
        u8 shift;
        u8 width;
};

struct ccu_div_config {
        u8 shift;
        u8 width;
};

struct ccu_mix {
        struct ccu_factor_config factor;
        struct ccu_gate_config gate;
        struct ccu_div_config div;
        struct ccu_mux_config mux;
        struct ccu_common common;
};

#define CCU_GATE_INIT(_mask)            { .mask = _mask }
#define CCU_FACTOR_INIT(_div, _mul)     { .div = _div, .mul = _mul }
#define CCU_MUX_INIT(_shift, _width)    { .shift = _shift, .width = _width }
#define CCU_DIV_INIT(_shift, _width)    { .shift = _shift, .width = _width }
#define CCU_GATE_FLAGS_INIT(_mask, _inverted)   { .mask = _mask, .inverted = _inverted }

#define CCU_PARENT_HW(_parent)          { .hw = &_parent.common.hw }
#define CCU_PARENT_NAME(_name)          { .fw_name = #_name }

#define CCU_MIX_INITHW(_name, _parent, _ops, _flags)                    \
        .hw.init = &(struct clk_init_data) {                            \
                .flags          = _flags,                               \
                .name           = #_name,                               \
                .parent_data    = (const struct clk_parent_data[])      \
                                        { _parent },                    \
                .num_parents    = 1,                                    \
                .ops            = &_ops,                                \
        }

#define CCU_MIX_INITHW_PARENTS(_name, _parents, _ops, _flags)           \
        .hw.init = CLK_HW_INIT_PARENTS_DATA(#_name, _parents, &_ops, _flags)

#define CCU_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _flags)          \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_gate_ops, _flags),  \
        }                                                                       \
}

#define CCU_FACTOR_DEFINE(_name, _parent, _div, _mul)                           \
static struct ccu_mix _name = {                                                 \
        .factor = CCU_FACTOR_INIT(_div, _mul),                                  \
        .common = {                                                             \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_ops, 0),     \
        }                                                                       \
}

#define CCU_MUX_DEFINE(_name, _parents, _reg_ctrl, _shift, _width, _flags)      \
static struct ccu_mix _name = {                                                 \
        .mux    = CCU_MUX_INIT(_shift, _width),                                 \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW_PARENTS(_name, _parents, spacemit_ccu_mux_ops,   \
                                       _flags),                                 \
        }                                                                       \
}

#define CCU_DIV_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, _flags)       \
static struct ccu_mix _name = {                                                 \
        .div    = CCU_DIV_INIT(_shift, _width),                                 \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_ops, _flags)    \
        }                                                                       \
}

#define CCU_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _inverted, _flags)         \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_FLAGS_INIT(_mask_gate, _inverted),                   \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_gate_ops, _flags),  \
        }                                                                       \
}

#define CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div,       \
                               _mul, _flags)                                    \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .factor = CCU_FACTOR_INIT(_div, _mul),                                  \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_gate_ops, _flags)    \
        }                                                                       \
}

#define CCU_FACTOR_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div,     \
                               _mul)                                            \
        CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div,       \
                               _mul, 0)

#define CCU_MUX_GATE_DEFINE(_name, _parents, _reg_ctrl, _shift, _width,         \
                            _mask_gate, _flags)                                 \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .mux    = CCU_MUX_INIT(_shift, _width),                                 \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW_PARENTS(_name, _parents,                         \
                                       spacemit_ccu_mux_gate_ops, _flags),      \
        }                                                                       \
}

#define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width,          \
                            _mask_gate, _flags)                                 \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .div    = CCU_DIV_INIT(_shift, _width),                                 \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops,       \
                               _flags),                                         \
        }                                                                       \
}

#define CCU_MUX_DIV_GATE_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth,   \
                                 _muxshift, _muxwidth, _mask_gate, _flags)      \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .div    = CCU_DIV_INIT(_mshift, _mwidth),                               \
        .mux    = CCU_MUX_INIT(_muxshift, _muxwidth),                           \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                CCU_MIX_INITHW_PARENTS(_name, _parents,                         \
                                       spacemit_ccu_mux_div_gate_ops, _flags),  \
        },                                                                      \
}

#define CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_name, _parents, _reg_ctrl, _reg_fc,   \
                                         _mshift, _mwidth, _mask_fc, _muxshift, \
                                         _muxwidth, _mask_gate, _flags)         \
static struct ccu_mix _name = {                                                 \
        .gate   = CCU_GATE_INIT(_mask_gate),                                    \
        .div    = CCU_DIV_INIT(_mshift, _mwidth),                               \
        .mux    = CCU_MUX_INIT(_muxshift, _muxwidth),                           \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                .reg_fc         = _reg_fc,                                      \
                .mask_fc        = _mask_fc,                                     \
                CCU_MIX_INITHW_PARENTS(_name, _parents,                         \
                                       spacemit_ccu_mux_div_gate_ops, _flags),  \
        },                                                                      \
}

#define CCU_MUX_DIV_GATE_FC_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth,\
                                   _mask_fc, _muxshift, _muxwidth, _mask_gate,  \
                                   _flags)                                      \
CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_name, _parents, _reg_ctrl, _reg_ctrl, _mshift,\
                                 _mwidth, _mask_fc, _muxshift, _muxwidth,       \
                                 _mask_gate, _flags)

#define CCU_MUX_DIV_FC_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth,     \
                              _mask_fc, _muxshift, _muxwidth, _flags)           \
static struct ccu_mix _name = {                                                 \
        .div    = CCU_DIV_INIT(_mshift, _mwidth),                               \
        .mux    = CCU_MUX_INIT(_muxshift, _muxwidth),                           \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                .reg_fc         = _reg_ctrl,                                    \
                .mask_fc        = _mask_fc,                                     \
                CCU_MIX_INITHW_PARENTS(_name, _parents,                         \
                                       spacemit_ccu_mux_div_ops, _flags),       \
        },                                                                      \
}

#define CCU_MUX_FC_DEFINE(_name, _parents, _reg_ctrl, _mask_fc, _muxshift,      \
                          _muxwidth, _flags)                                    \
static struct ccu_mix _name = {                                                 \
        .mux    = CCU_MUX_INIT(_muxshift, _muxwidth),                           \
        .common = {                                                             \
                .reg_ctrl       = _reg_ctrl,                                    \
                .reg_fc         = _reg_ctrl,                                    \
                .mask_fc        = _mask_fc,                                     \
                CCU_MIX_INITHW_PARENTS(_name, _parents, spacemit_ccu_mux_ops,   \
                                       _flags)                                  \
        },                                                                      \
}

static inline struct ccu_mix *hw_to_ccu_mix(struct clk_hw *hw)
{
        struct ccu_common *common = hw_to_ccu_common(hw);

        return container_of(common, struct ccu_mix, common);
}

extern const struct clk_ops spacemit_ccu_gate_ops;
extern const struct clk_ops spacemit_ccu_factor_ops;
extern const struct clk_ops spacemit_ccu_mux_ops;
extern const struct clk_ops spacemit_ccu_div_ops;
extern const struct clk_ops spacemit_ccu_factor_gate_ops;
extern const struct clk_ops spacemit_ccu_div_gate_ops;
extern const struct clk_ops spacemit_ccu_mux_gate_ops;
extern const struct clk_ops spacemit_ccu_mux_div_ops;
extern const struct clk_ops spacemit_ccu_mux_div_gate_ops;
#endif /* _CCU_MIX_H_ */