root/drivers/clk/sophgo/clk-sg2042-rpgate.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Sophgo SG2042 RP clock Driver
 *
 * Copyright (C) 2024 Sophgo Technology Inc.
 * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
 */

#include <linux/array_size.h>
#include <linux/clk-provider.h>
#include <linux/platform_device.h>

#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>

#include "clk-sg2042.h"

#define R_SYSGATE_BEGIN         0x0368
#define R_RP_RXU_CLK_ENABLE     (0x0368 - R_SYSGATE_BEGIN)
#define R_MP0_STATUS_REG        (0x0380 - R_SYSGATE_BEGIN)
#define R_MP0_CONTROL_REG       (0x0384 - R_SYSGATE_BEGIN)
#define R_MP1_STATUS_REG        (0x0388 - R_SYSGATE_BEGIN)
#define R_MP1_CONTROL_REG       (0x038C - R_SYSGATE_BEGIN)
#define R_MP2_STATUS_REG        (0x0390 - R_SYSGATE_BEGIN)
#define R_MP2_CONTROL_REG       (0x0394 - R_SYSGATE_BEGIN)
#define R_MP3_STATUS_REG        (0x0398 - R_SYSGATE_BEGIN)
#define R_MP3_CONTROL_REG       (0x039C - R_SYSGATE_BEGIN)
#define R_MP4_STATUS_REG        (0x03A0 - R_SYSGATE_BEGIN)
#define R_MP4_CONTROL_REG       (0x03A4 - R_SYSGATE_BEGIN)
#define R_MP5_STATUS_REG        (0x03A8 - R_SYSGATE_BEGIN)
#define R_MP5_CONTROL_REG       (0x03AC - R_SYSGATE_BEGIN)
#define R_MP6_STATUS_REG        (0x03B0 - R_SYSGATE_BEGIN)
#define R_MP6_CONTROL_REG       (0x03B4 - R_SYSGATE_BEGIN)
#define R_MP7_STATUS_REG        (0x03B8 - R_SYSGATE_BEGIN)
#define R_MP7_CONTROL_REG       (0x03BC - R_SYSGATE_BEGIN)
#define R_MP8_STATUS_REG        (0x03C0 - R_SYSGATE_BEGIN)
#define R_MP8_CONTROL_REG       (0x03C4 - R_SYSGATE_BEGIN)
#define R_MP9_STATUS_REG        (0x03C8 - R_SYSGATE_BEGIN)
#define R_MP9_CONTROL_REG       (0x03CC - R_SYSGATE_BEGIN)
#define R_MP10_STATUS_REG       (0x03D0 - R_SYSGATE_BEGIN)
#define R_MP10_CONTROL_REG      (0x03D4 - R_SYSGATE_BEGIN)
#define R_MP11_STATUS_REG       (0x03D8 - R_SYSGATE_BEGIN)
#define R_MP11_CONTROL_REG      (0x03DC - R_SYSGATE_BEGIN)
#define R_MP12_STATUS_REG       (0x03E0 - R_SYSGATE_BEGIN)
#define R_MP12_CONTROL_REG      (0x03E4 - R_SYSGATE_BEGIN)
#define R_MP13_STATUS_REG       (0x03E8 - R_SYSGATE_BEGIN)
#define R_MP13_CONTROL_REG      (0x03EC - R_SYSGATE_BEGIN)
#define R_MP14_STATUS_REG       (0x03F0 - R_SYSGATE_BEGIN)
#define R_MP14_CONTROL_REG      (0x03F4 - R_SYSGATE_BEGIN)
#define R_MP15_STATUS_REG       (0x03F8 - R_SYSGATE_BEGIN)
#define R_MP15_CONTROL_REG      (0x03FC - R_SYSGATE_BEGIN)

/**
 * struct sg2042_rpgate_clock - Gate clock for RP(riscv processors) subsystem
 * @hw:                 clk_hw for initialization
 * @id:                 used to map clk_onecell_data
 * @offset_enable:      offset of gate enable registers
 * @bit_idx:            which bit in the register controls gating of this clock
 */
struct sg2042_rpgate_clock {
        struct clk_hw hw;

        unsigned int id;

        u32 offset_enable;
        u8 bit_idx;
};

/*
 * Clock initialization macro naming rules:
 * FW: use CLK_HW_INIT_FW_NAME
 */
#define SG2042_GATE_FW(_id, _name, _parent, _flags,     \
                       _r_enable, _bit_idx) {           \
                .hw.init = CLK_HW_INIT_FW_NAME(         \
                                _name,                  \
                                _parent,                \
                                NULL,                   \
                                _flags),                \
                .id = _id,                              \
                .offset_enable = _r_enable,             \
                .bit_idx = _bit_idx,                    \
        }

/*
 * Gate clocks for RP subsystem (including the MP subsystem), which control
 * registers are defined in SYS_CTRL.
 */
static const struct sg2042_rpgate_clock sg2042_gate_rp[] = {
        /* downstream of clk_gate_rp_cpu_normal about rxu */
        SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 0),
        SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 1),
        SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 2),
        SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 3),
        SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 4),
        SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 5),
        SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 6),
        SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 7),
        SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 8),
        SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 9),
        SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 10),
        SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 11),
        SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 12),
        SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 13),
        SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 14),
        SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 15),
        SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 16),
        SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 17),
        SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 18),
        SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 19),
        SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 20),
        SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 21),
        SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 22),
        SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 23),
        SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 24),
        SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 25),
        SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 26),
        SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 27),
        SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 28),
        SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 29),
        SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 30),
        SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
                       0, R_RP_RXU_CLK_ENABLE, 31),

        /* downstream of clk_gate_rp_cpu_normal about mp */
        SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
                       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
                       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
                       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
                       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
                       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
                       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
                       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
                       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
                       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
                       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
                       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
                       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
                       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
                       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
                       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
        SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
                       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
};

static DEFINE_SPINLOCK(sg2042_clk_lock);

static int sg2042_clk_register_rpgates(struct device *dev,
                                       struct sg2042_clk_data *clk_data,
                                       const struct sg2042_rpgate_clock gate_clks[],
                                       int num_gate_clks)
{
        const struct sg2042_rpgate_clock *gate;
        struct clk_hw *hw;
        int i, ret = 0;

        for (i = 0; i < num_gate_clks; i++) {
                gate = &gate_clks[i];
                hw = devm_clk_hw_register_gate_parent_data
                        (dev,
                         gate->hw.init->name,
                         gate->hw.init->parent_data,
                         gate->hw.init->flags,
                         clk_data->iobase + gate->offset_enable,
                         gate->bit_idx,
                         0,
                         &sg2042_clk_lock);
                if (IS_ERR(hw)) {
                        pr_err("failed to register clock %s\n", gate->hw.init->name);
                        ret = PTR_ERR(hw);
                        break;
                }

                clk_data->onecell_data.hws[gate->id] = hw;
        }

        return ret;
}

static int sg2042_init_clkdata(struct platform_device *pdev,
                               int num_clks,
                               struct sg2042_clk_data **pp_clk_data)
{
        struct sg2042_clk_data *clk_data;

        clk_data = devm_kzalloc(&pdev->dev,
                                struct_size(clk_data, onecell_data.hws, num_clks),
                                GFP_KERNEL);
        if (!clk_data)
                return -ENOMEM;

        clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
        if (WARN_ON(IS_ERR(clk_data->iobase)))
                return PTR_ERR(clk_data->iobase);

        clk_data->onecell_data.num = num_clks;

        *pp_clk_data = clk_data;

        return 0;
}

static int sg2042_rpgate_probe(struct platform_device *pdev)
{
        struct sg2042_clk_data *clk_data = NULL;
        int num_clks;
        int ret;

        num_clks = ARRAY_SIZE(sg2042_gate_rp);

        ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
        if (ret)
                goto error_out;

        ret = sg2042_clk_register_rpgates(&pdev->dev, clk_data, sg2042_gate_rp,
                                          num_clks);
        if (ret)
                goto error_out;

        return devm_of_clk_add_hw_provider(&pdev->dev,
                                           of_clk_hw_onecell_get,
                                           &clk_data->onecell_data);

error_out:
        pr_err("%s failed error number %d\n", __func__, ret);
        return ret;
}

static const struct of_device_id sg2042_rpgate_match[] = {
        { .compatible = "sophgo,sg2042-rpgate" },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sg2042_rpgate_match);

static struct platform_driver sg2042_rpgate_driver = {
        .probe = sg2042_rpgate_probe,
        .driver = {
                .name = "clk-sophgo-sg2042-rpgate",
                .of_match_table = sg2042_rpgate_match,
                .suppress_bind_attrs = true,
        },
};
module_platform_driver(sg2042_rpgate_driver);

MODULE_AUTHOR("Chen Wang");
MODULE_DESCRIPTION("Sophgo SG2042 rp subsystem clock driver");
MODULE_LICENSE("GPL");