mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
clk: sophgo: Add SG2042 clock driver
Add a driver for the SOPHGO SG2042 clocks. Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
This commit is contained in:
parent
5911423798
commit
48cf7e0138
@ -9,3 +9,31 @@ config CLK_SOPHGO_CV1800
|
||||
The driver require a 25MHz Oscillator to function generate clock.
|
||||
It includes PLLs, common clock function and some vendor clock for
|
||||
IPs of CV18XX series SoC
|
||||
|
||||
config CLK_SOPHGO_SG2042_PLL
|
||||
tristate "Sophgo SG2042 PLL clock support"
|
||||
depends on ARCH_SOPHGO || COMPILE_TEST
|
||||
help
|
||||
This driver supports the PLL clock controller on the
|
||||
Sophgo SG2042 SoC. This clock IP uses three oscillators with
|
||||
frequency of 25 MHz as input, which are used for Main/Fixed
|
||||
PLL, DDR PLL 0 and DDR PLL 1 respectively.
|
||||
|
||||
config CLK_SOPHGO_SG2042_CLKGEN
|
||||
tristate "Sophgo SG2042 Clock Generator support"
|
||||
depends on CLK_SOPHGO_SG2042_PLL
|
||||
help
|
||||
This driver supports the Clock Generator on the
|
||||
Sophgo SG2042 SoC. This clock IP depends on SG2042 PLL clock
|
||||
because it uses PLL clocks as input.
|
||||
This driver provides clock function such as DIV/Mux/Gate.
|
||||
|
||||
config CLK_SOPHGO_SG2042_RPGATE
|
||||
tristate "Sophgo SG2042 RP subsystem clock controller support"
|
||||
depends on CLK_SOPHGO_SG2042_CLKGEN
|
||||
help
|
||||
This driver supports the RP((Riscv Processors)) subsystem clock
|
||||
controller on the Sophgo SG2042 SoC.
|
||||
This clock IP depends on SG2042 Clock Generator because it uses
|
||||
clock from Clock Generator IP as input.
|
||||
This driver provides Gate function for RP.
|
||||
|
@ -5,3 +5,7 @@ clk-sophgo-cv1800-y += clk-cv1800.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-common.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-ip.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-pll.o
|
||||
|
||||
obj-$(CONFIG_CLK_SOPHGO_SG2042_CLKGEN) += clk-sg2042-clkgen.o
|
||||
obj-$(CONFIG_CLK_SOPHGO_SG2042_PLL) += clk-sg2042-pll.o
|
||||
obj-$(CONFIG_CLK_SOPHGO_SG2042_RPGATE) += clk-sg2042-rpgate.o
|
||||
|
1152
drivers/clk/sophgo/clk-sg2042-clkgen.c
Normal file
1152
drivers/clk/sophgo/clk-sg2042-clkgen.c
Normal file
File diff suppressed because it is too large
Load Diff
570
drivers/clk/sophgo/clk-sg2042-pll.c
Normal file
570
drivers/clk/sophgo/clk-sg2042-pll.c
Normal file
@ -0,0 +1,570 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Sophgo SG2042 PLL clock Driver
|
||||
*
|
||||
* Copyright (C) 2024 Sophgo Technology Inc.
|
||||
* Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include <dt-bindings/clock/sophgo,sg2042-pll.h>
|
||||
|
||||
#include "clk-sg2042.h"
|
||||
|
||||
/* Registers defined in SYS_CTRL */
|
||||
#define R_PLL_BEGIN 0xC0
|
||||
#define R_PLL_STAT (0xC0 - R_PLL_BEGIN)
|
||||
#define R_PLL_CLKEN_CONTROL (0xC4 - R_PLL_BEGIN)
|
||||
#define R_MPLL_CONTROL (0xE8 - R_PLL_BEGIN)
|
||||
#define R_FPLL_CONTROL (0xF4 - R_PLL_BEGIN)
|
||||
#define R_DPLL0_CONTROL (0xF8 - R_PLL_BEGIN)
|
||||
#define R_DPLL1_CONTROL (0xFC - R_PLL_BEGIN)
|
||||
|
||||
/**
|
||||
* struct sg2042_pll_clock - PLL clock
|
||||
* @hw: clk_hw for initialization
|
||||
* @id: used to map clk_onecell_data
|
||||
* @base: used for readl/writel.
|
||||
* **NOTE**: PLL registers are all in SYS_CTRL!
|
||||
* @lock: spinlock to protect register access, modification
|
||||
* of frequency can only be served one at the time.
|
||||
* @offset_ctrl: offset of pll control registers
|
||||
* @shift_status_lock: shift of XXX_LOCK in pll status register
|
||||
* @shift_status_updating: shift of UPDATING_XXX in pll status register
|
||||
* @shift_enable: shift of XXX_CLK_EN in pll enable register
|
||||
*/
|
||||
struct sg2042_pll_clock {
|
||||
struct clk_hw hw;
|
||||
|
||||
unsigned int id;
|
||||
void __iomem *base;
|
||||
/* protect register access */
|
||||
spinlock_t *lock;
|
||||
|
||||
u32 offset_ctrl;
|
||||
u8 shift_status_lock;
|
||||
u8 shift_status_updating;
|
||||
u8 shift_enable;
|
||||
};
|
||||
|
||||
#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
|
||||
|
||||
#define KHZ 1000UL
|
||||
#define MHZ (KHZ * KHZ)
|
||||
|
||||
#define REFDIV_MIN 1
|
||||
#define REFDIV_MAX 63
|
||||
#define FBDIV_MIN 16
|
||||
#define FBDIV_MAX 320
|
||||
|
||||
#define PLL_FREF_SG2042 (25 * MHZ)
|
||||
|
||||
#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
|
||||
#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
|
||||
|
||||
#define PLL_FOUTVCO_MIN (800 * MHZ)
|
||||
#define PLL_FOUTVCO_MAX (3200 * MHZ)
|
||||
|
||||
struct sg2042_pll_ctrl {
|
||||
unsigned long freq;
|
||||
unsigned int fbdiv;
|
||||
unsigned int postdiv1;
|
||||
unsigned int postdiv2;
|
||||
unsigned int refdiv;
|
||||
};
|
||||
|
||||
#define PLLCTRL_FBDIV_MASK GENMASK(27, 16)
|
||||
#define PLLCTRL_POSTDIV2_MASK GENMASK(14, 12)
|
||||
#define PLLCTRL_POSTDIV1_MASK GENMASK(10, 8)
|
||||
#define PLLCTRL_REFDIV_MASK GENMASK(5, 0)
|
||||
|
||||
static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
|
||||
{
|
||||
return FIELD_PREP(PLLCTRL_FBDIV_MASK, ctrl->fbdiv) |
|
||||
FIELD_PREP(PLLCTRL_POSTDIV2_MASK, ctrl->postdiv2) |
|
||||
FIELD_PREP(PLLCTRL_POSTDIV1_MASK, ctrl->postdiv1) |
|
||||
FIELD_PREP(PLLCTRL_REFDIV_MASK, ctrl->refdiv);
|
||||
}
|
||||
|
||||
static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
|
||||
struct sg2042_pll_ctrl *ctrl)
|
||||
{
|
||||
ctrl->fbdiv = FIELD_GET(PLLCTRL_FBDIV_MASK, reg_value);
|
||||
ctrl->refdiv = FIELD_GET(PLLCTRL_REFDIV_MASK, reg_value);
|
||||
ctrl->postdiv1 = FIELD_GET(PLLCTRL_POSTDIV1_MASK, reg_value);
|
||||
ctrl->postdiv2 = FIELD_GET(PLLCTRL_POSTDIV2_MASK, reg_value);
|
||||
}
|
||||
|
||||
static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
if (en) {
|
||||
/* wait pll lock */
|
||||
if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT,
|
||||
value,
|
||||
((value >> pll->shift_status_lock) & 0x1),
|
||||
0,
|
||||
100000))
|
||||
pr_warn("%s not locked\n", pll->hw.init->name);
|
||||
|
||||
/* wait pll updating */
|
||||
if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT,
|
||||
value,
|
||||
!((value >> pll->shift_status_updating) & 0x1),
|
||||
0,
|
||||
100000))
|
||||
pr_warn("%s still updating\n", pll->hw.init->name);
|
||||
|
||||
/* enable pll */
|
||||
value = readl(pll->base + R_PLL_CLKEN_CONTROL);
|
||||
writel(value | (1 << pll->shift_enable), pll->base + R_PLL_CLKEN_CONTROL);
|
||||
} else {
|
||||
/* disable pll */
|
||||
value = readl(pll->base + R_PLL_CLKEN_CONTROL);
|
||||
writel(value & (~(1 << pll->shift_enable)), pll->base + R_PLL_CLKEN_CONTROL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sg2042_pll_recalc_rate() - Calculate rate for plls
|
||||
* @reg_value: current register value
|
||||
* @parent_rate: parent frequency
|
||||
*
|
||||
* This function is used to calculate below "rate" in equation
|
||||
* rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
|
||||
* = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
|
||||
*
|
||||
* Return: The rate calculated.
|
||||
*/
|
||||
static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sg2042_pll_ctrl ctrl_table;
|
||||
u64 numerator, denominator;
|
||||
|
||||
sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
|
||||
|
||||
numerator = parent_rate * ctrl_table.fbdiv;
|
||||
denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
|
||||
do_div(numerator, denominator);
|
||||
return numerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
|
||||
* look up the postdiv1_2 table to get the closest postdiiv combination.
|
||||
* @rate: FOUTPOSTDIV
|
||||
* @prate: parent rate, i.e. FREF
|
||||
* @fbdiv: FBDIV
|
||||
* @refdiv: REFDIV
|
||||
* @postdiv1: POSTDIV1, output
|
||||
* @postdiv2: POSTDIV2, output
|
||||
*
|
||||
* postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
|
||||
* for example:
|
||||
* postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
|
||||
*
|
||||
* See TRM:
|
||||
* FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
|
||||
* So we get following formula to get POSTDIV1 and POSTDIV2:
|
||||
* POSTDIV = (prate/REFDIV) x FBDIV/rate
|
||||
* above POSTDIV = POSTDIV1*POSTDIV2
|
||||
*
|
||||
* Return:
|
||||
* %0 - OK
|
||||
* %-EINVAL - invalid argument, which means Failed to get the postdivs.
|
||||
*/
|
||||
static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
|
||||
unsigned long prate,
|
||||
unsigned int fbdiv,
|
||||
unsigned int refdiv,
|
||||
unsigned int *postdiv1,
|
||||
unsigned int *postdiv2)
|
||||
{
|
||||
int index;
|
||||
u64 tmp0;
|
||||
|
||||
/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
|
||||
#define POSTDIV_RESULT_INDEX 2
|
||||
|
||||
static const int postdiv1_2[][3] = {
|
||||
{2, 4, 8}, {3, 3, 9}, {2, 5, 10}, {2, 6, 12},
|
||||
{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
|
||||
{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
|
||||
{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
|
||||
{6, 7, 42}, {7, 7, 49}
|
||||
};
|
||||
|
||||
/* prate/REFDIV and result save to tmp0 */
|
||||
tmp0 = prate;
|
||||
do_div(tmp0, refdiv);
|
||||
|
||||
/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
|
||||
tmp0 *= fbdiv;
|
||||
|
||||
/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
|
||||
do_div(tmp0, rate);
|
||||
|
||||
/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
|
||||
if (tmp0 <= 7) {
|
||||
/* (div1 * div2) <= 7, no need to use array search */
|
||||
*postdiv1 = tmp0;
|
||||
*postdiv2 = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* (div1 * div2) > 7, use array search */
|
||||
for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
|
||||
if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
|
||||
continue;
|
||||
} else {
|
||||
/* found it */
|
||||
*postdiv1 = postdiv1_2[index][1];
|
||||
*postdiv2 = postdiv1_2[index][0];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pr_warn("%s can not find in postdiv array!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
|
||||
* FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
|
||||
* register.
|
||||
* @req_rate: expected output clock rate, i.e. FOUTPISTDIV
|
||||
* @parent_rate: input parent clock rate, i.e. FREF
|
||||
* @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
|
||||
*
|
||||
* Return:
|
||||
* %0 - OK
|
||||
* %-EINVAL - invalid argument
|
||||
*/
|
||||
static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
|
||||
unsigned long req_rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned int fbdiv, refdiv, postdiv1, postdiv2;
|
||||
unsigned long foutpostdiv;
|
||||
u64 foutvco;
|
||||
int ret;
|
||||
u64 tmp;
|
||||
|
||||
if (parent_rate != PLL_FREF_SG2042) {
|
||||
pr_err("INVALID FREF: %ld\n", parent_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
|
||||
pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(best, 0, sizeof(struct sg2042_pll_ctrl));
|
||||
|
||||
for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
|
||||
/* required by hardware: FREF/REFDIV must > 10 */
|
||||
tmp = parent_rate;
|
||||
do_div(tmp, refdiv);
|
||||
if (tmp <= 10)
|
||||
continue;
|
||||
|
||||
for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
|
||||
/*
|
||||
* FOUTVCO = FREF*FBDIV/REFDIV validation
|
||||
* required by hardware, FOUTVCO must [800MHz, 3200MHz]
|
||||
*/
|
||||
foutvco = parent_rate * fbdiv;
|
||||
do_div(foutvco, refdiv);
|
||||
if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
|
||||
continue;
|
||||
|
||||
ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
|
||||
fbdiv, refdiv,
|
||||
&postdiv1, &postdiv2);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
|
||||
* = FOUTVCO/(POSTDIV1*POSTDIV2)
|
||||
*/
|
||||
tmp = foutvco;
|
||||
do_div(tmp, (postdiv1 * postdiv2));
|
||||
foutpostdiv = (unsigned long)tmp;
|
||||
/* Iterative to approach the expected value */
|
||||
if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
|
||||
best->freq = foutpostdiv;
|
||||
best->refdiv = refdiv;
|
||||
best->fbdiv = fbdiv;
|
||||
best->postdiv1 = postdiv1;
|
||||
best->postdiv2 = postdiv2;
|
||||
if (foutpostdiv == req_rate)
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (best->freq == 0)
|
||||
return -EINVAL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
|
||||
* @hw: ccf use to hook get sg2042_pll_clock
|
||||
* @parent_rate: parent rate
|
||||
*
|
||||
* The is function will be called through clk_get_rate
|
||||
* and return current rate after decoding reg value
|
||||
*
|
||||
* Return: Current rate recalculated.
|
||||
*/
|
||||
static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
|
||||
unsigned long rate;
|
||||
u32 value;
|
||||
|
||||
value = readl(pll->base + pll->offset_ctrl);
|
||||
rate = sg2042_pll_recalc_rate(value, parent_rate);
|
||||
|
||||
pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
|
||||
clk_hw_get_name(hw), rate);
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
|
||||
unsigned long req_rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct sg2042_pll_ctrl pctrl_table;
|
||||
unsigned int value;
|
||||
long proper_rate;
|
||||
int ret;
|
||||
|
||||
ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
|
||||
if (ret) {
|
||||
proper_rate = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
value = sg2042_pll_ctrl_encode(&pctrl_table);
|
||||
proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
|
||||
|
||||
out:
|
||||
pr_debug("--> %s: pll_round_rate: val = %ld\n",
|
||||
clk_hw_get_name(hw), proper_rate);
|
||||
return proper_rate;
|
||||
}
|
||||
|
||||
static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
|
||||
&req->best_parent_rate);
|
||||
pr_debug("--> %s: pll_determine_rate: val = %ld\n",
|
||||
clk_hw_get_name(hw), req->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
|
||||
struct sg2042_pll_ctrl pctrl_table;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(pll->lock, flags);
|
||||
if (sg2042_pll_enable(pll, 0)) {
|
||||
pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
|
||||
goto out;
|
||||
}
|
||||
ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
|
||||
if (ret) {
|
||||
pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
value = sg2042_pll_ctrl_encode(&pctrl_table);
|
||||
|
||||
/* write the value to top register */
|
||||
writel(value, pll->base + pll->offset_ctrl);
|
||||
|
||||
out2:
|
||||
sg2042_pll_enable(pll, 1);
|
||||
out:
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
|
||||
pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
|
||||
clk_hw_get_name(hw), value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops sg2042_clk_pll_ops = {
|
||||
.recalc_rate = sg2042_clk_pll_recalc_rate,
|
||||
.round_rate = sg2042_clk_pll_round_rate,
|
||||
.determine_rate = sg2042_clk_pll_determine_rate,
|
||||
.set_rate = sg2042_clk_pll_set_rate,
|
||||
};
|
||||
|
||||
static const struct clk_ops sg2042_clk_pll_ro_ops = {
|
||||
.recalc_rate = sg2042_clk_pll_recalc_rate,
|
||||
.round_rate = sg2042_clk_pll_round_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock initialization macro naming rules:
|
||||
* FW: use CLK_HW_INIT_FW_NAME
|
||||
* RO: means Read-Only
|
||||
*/
|
||||
#define SG2042_PLL_FW(_id, _name, _parent, _r_ctrl, _shift) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.hw.init = CLK_HW_INIT_FW_NAME( \
|
||||
_name, \
|
||||
_parent, \
|
||||
&sg2042_clk_pll_ops, \
|
||||
CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
|
||||
.offset_ctrl = _r_ctrl, \
|
||||
.shift_status_lock = 8 + (_shift), \
|
||||
.shift_status_updating = _shift, \
|
||||
.shift_enable = _shift, \
|
||||
}
|
||||
|
||||
#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_ctrl, _shift) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.hw.init = CLK_HW_INIT_FW_NAME( \
|
||||
_name, \
|
||||
_parent, \
|
||||
&sg2042_clk_pll_ro_ops, \
|
||||
CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
|
||||
.offset_ctrl = _r_ctrl, \
|
||||
.shift_status_lock = 8 + (_shift), \
|
||||
.shift_status_updating = _shift, \
|
||||
.shift_enable = _shift, \
|
||||
}
|
||||
|
||||
static struct sg2042_pll_clock sg2042_pll_clks[] = {
|
||||
SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main", R_MPLL_CONTROL, 0),
|
||||
SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main", R_FPLL_CONTROL, 3),
|
||||
SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0", R_DPLL0_CONTROL, 4),
|
||||
SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1", R_DPLL1_CONTROL, 5),
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(sg2042_clk_lock);
|
||||
|
||||
static int sg2042_clk_register_plls(struct device *dev,
|
||||
struct sg2042_clk_data *clk_data,
|
||||
struct sg2042_pll_clock pll_clks[],
|
||||
int num_pll_clks)
|
||||
{
|
||||
struct sg2042_pll_clock *pll;
|
||||
struct clk_hw *hw;
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < num_pll_clks; i++) {
|
||||
pll = &pll_clks[i];
|
||||
/* assign these for ops usage during registration */
|
||||
pll->base = clk_data->iobase;
|
||||
pll->lock = &sg2042_clk_lock;
|
||||
|
||||
hw = &pll->hw;
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
pr_err("failed to register clock %s\n", pll->hw.init->name);
|
||||
break;
|
||||
}
|
||||
|
||||
clk_data->onecell_data.hws[pll->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_pll_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sg2042_clk_data *clk_data = NULL;
|
||||
int num_clks;
|
||||
int ret;
|
||||
|
||||
num_clks = ARRAY_SIZE(sg2042_pll_clks);
|
||||
|
||||
ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
|
||||
ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
|
||||
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_pll_match[] = {
|
||||
{ .compatible = "sophgo,sg2042-pll" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sg2042_pll_match);
|
||||
|
||||
static struct platform_driver sg2042_pll_driver = {
|
||||
.probe = sg2042_pll_probe,
|
||||
.driver = {
|
||||
.name = "clk-sophgo-sg2042-pll",
|
||||
.of_match_table = sg2042_pll_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sg2042_pll_driver);
|
||||
|
||||
MODULE_AUTHOR("Chen Wang");
|
||||
MODULE_DESCRIPTION("Sophgo SG2042 pll clock driver");
|
||||
MODULE_LICENSE("GPL");
|
291
drivers/clk/sophgo/clk-sg2042-rpgate.c
Normal file
291
drivers/clk/sophgo/clk-sg2042-rpgate.c
Normal file
@ -0,0 +1,291 @@
|
||||
// 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");
|
18
drivers/clk/sophgo/clk-sg2042.h
Normal file
18
drivers/clk/sophgo/clk-sg2042.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _CLK_SOPHGO_SG2042_H_
|
||||
#define _CLK_SOPHGO_SG2042_H_
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
/**
|
||||
* struct sg2042_clk_data - Common data of clock-controller
|
||||
* @iobase: base address of clock-controller
|
||||
* @onecell_data: used for adding providers.
|
||||
*/
|
||||
struct sg2042_clk_data {
|
||||
void __iomem *iobase;
|
||||
struct clk_hw_onecell_data onecell_data;
|
||||
};
|
||||
|
||||
#endif /* _CLK_SOPHGO_SG2042_H_ */
|
Loading…
Reference in New Issue
Block a user