Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled

This commit is contained in:
domenico
2025-06-24 14:35:53 +02:00
commit c06fb25d1f
9263 changed files with 1750214 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_MACH_SIFLOWER_KMALLOC_H
#define __ASM_MACH_SIFLOWER_KMALLOC_H
#ifdef CONFIG_DMA_NONCOHERENT
#define ARCH_DMA_MINALIGN 32
#endif
#endif /* __ASM_MACH_SIFLOWER_KMALLOC_H */

View File

@@ -0,0 +1,27 @@
# SPDX-License-Identifier: GPL-2.0
menuconfig CLK_SIFLOWER
bool "Siflower SoC driver support"
depends on MIPS || COMPILE_TEST
help
SoC drivers for Siflower Linux-capable SoCs.
if CLK_SIFLOWER
config CLK_SF19A2890
bool "Clock driver for Siflower CLK_SF19A2890"
depends on MIPS || COMPILE_TEST
help
Supports the Top Clock Module found in SF19A2890. If this
kernel is meant to run on a Siflower SF19A2890 SoC,
enable this driver.
config CLK_SF19A2890_PERIPH
bool "Clock driver for Siflower SF19A2890 peripheral clock gates"
depends on MIPS || COMPILE_TEST
help
Supports the clock gates for various peripherals in SF19A2890.
If this kernel is meant to run on a Siflower SF19A2890 SoC,
enable this driver.
endif

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_CLK_SF19A2890) += clk-sf19a2890.o
obj-$(CONFIG_CLK_SF19A2890_PERIPH) += clk-sf19a2890-periph.o

View File

@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/of_clk.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#define REG_GATE 0x0
/*
* A shared 'Bus Output Enable' signal for all APB peripherals. The peripheral
* only responds to bus requests if its dedicated clock is enabled and this
* shared BOE is set.
*/
#define REG_BOE 0x8
#define BOE_EN GENMASK(1, 0)
struct sf19a2890_periphclk {
void __iomem *base;
struct clk_hw hw;
u32 idx;
};
struct sf19a2890_periphclk_priv {
struct sf19a2890_periphclk *gates;
struct clk_hw_onecell_data clk_data;
};
static inline struct sf19a2890_periphclk *hw_to_periphclk(struct clk_hw *hw)
{
return container_of(hw, struct sf19a2890_periphclk, hw);
}
static int sf19a2890_periphclk_enable(struct clk_hw *hw)
{
struct sf19a2890_periphclk *priv = hw_to_periphclk(hw);
u32 reg = readl(priv->base + REG_GATE);
writel(reg | BIT(priv->idx), priv->base + REG_GATE);
writel(BOE_EN, priv->base + REG_BOE);
return 0;
}
static void sf19a2890_periphclk_disable(struct clk_hw *hw)
{
struct sf19a2890_periphclk *priv = hw_to_periphclk(hw);
u32 reg = readl(priv->base + REG_GATE);
reg &= ~BIT(priv->idx);
writel(reg, priv->base + REG_GATE);
if (reg == 0)
writel(0, priv->base + REG_BOE);
}
static int sf19a2890_periphclk_is_enabled(struct clk_hw *hw)
{
struct sf19a2890_periphclk *priv = hw_to_periphclk(hw);
return !!(readl(priv->base + REG_GATE) & BIT(priv->idx));
}
static const struct clk_ops sf19a28_periphclk_ops = {
.enable = sf19a2890_periphclk_enable,
.disable = sf19a2890_periphclk_disable,
.is_enabled = sf19a2890_periphclk_is_enabled,
};
static void __init sf19a2890_periphclk_init(struct device_node *node)
{
struct clk_init_data init = {};
struct sf19a2890_periphclk_priv *priv;
u32 reg, valid_gates, critical_gates;
int num_clks, i, ret, idx;
const char *name, *parent;
void __iomem *base;
num_clks = of_count_phandle_with_args(node, "clocks", "#clock-cells");
if (num_clks < 1 || num_clks > 32)
return;
ret = of_property_read_u32(node, "siflower,valid-gates", &valid_gates);
if (ret < 0)
valid_gates = BIT(num_clks) - 1;
ret = of_property_read_u32(node, "siflower,critical-gates", &critical_gates);
if (ret < 0)
critical_gates = 0;
priv = kzalloc(struct_size(priv, clk_data.hws, num_clks), GFP_KERNEL);
if (!priv)
return;
priv->clk_data.num = num_clks;
priv->gates = kcalloc(num_clks, sizeof(struct sf19a2890_periphclk),
GFP_KERNEL);
if (!priv->gates)
goto err1;
base = of_iomap(node, 0);
if (!base) {
pr_err("failed to map resources.\n");
goto err2;
}
/* clear unused higher bits for BOE check in disable call. */
reg = readl(base + REG_GATE);
reg &= valid_gates;
writel(reg, base + REG_GATE);
for (i = 0, idx = 0; i < num_clks && idx < 32; i++, idx++) {
ret = of_property_read_string_index(node, "clock-output-names",
i, &name);
if (ret != 0) {
pr_err("failed to read output name for the %dth gate.\n",
i);
goto err3;
}
parent = of_clk_get_parent_name(node, i);
if (!parent) {
pr_err("failed to get parent clock for the %dth gate.\n",
i);
goto err3;
}
while (!(valid_gates & BIT(idx))) {
idx++;
if (idx >= 32) {
pr_err("too few valid gates.");
goto err3;
}
}
priv->gates[i].base = base;
priv->gates[i].idx = idx;
init.name = name;
init.ops = &sf19a28_periphclk_ops;
init.parent_names = &parent;
init.num_parents = 1;
init.flags = (critical_gates & BIT(idx)) ? CLK_IS_CRITICAL : 0;
priv->gates[i].hw.init = &init;
ret = clk_hw_register(NULL, &priv->gates[i].hw);
if (ret) {
pr_err("failed to register the %dth gate: %d.\n", i,
ret);
goto err3;
}
priv->clk_data.hws[i] = &priv->gates[i].hw;
}
ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
&priv->clk_data);
if (ret) {
pr_err("failed to add hw provider.\n");
goto err3;
}
return;
err3:
for (i--; i >= 0; i--)
clk_hw_unregister_gate(priv->clk_data.hws[i]);
err2:
kfree(priv->gates);
err1:
kfree(priv);
}
CLK_OF_DECLARE(sf19a2890_periphclk, "siflower,sf19a2890-periph-clk",
sf19a2890_periphclk_init);

View File

@@ -0,0 +1,414 @@
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/compiler.h>
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/bug.h>
#include <dt-bindings/clock/siflower,sf19a2890-clk.h>
#define REG_PLL_BASE 0x0
#define REG_PLL_PD 0x0
#define PLL_PD BIT(0)
#define PLL_PD_VCO BIT(1)
#define PLL_PD_POSTDIV BIT(2)
#define PLL_PD_4PHASE BIT(3)
#define PLL_PD_DAC BIT(4)
#define PLL_PD_DSM BIT(5)
/*
* PLL_PARAM is a 48-bit value put into 6 registers, 8-bit per register:
* REFDIV = PLL_PARAM[47:42]
* POSTDIV2 = PLL_PARAM[41:39]
* POSTDIV1 = PLL_PARAM[38:36]
* FRAC = PLL_PARAM[35:12]
* FBDIV = PLL_PARAM[11:0]
*/
#define REG_PLL_PARAM(_x) (0x4 + (_x) * 4)
#define PLL_REFDIV_HI 47
#define PLL_REFDIV_LO 42
#define PLL_POSTDIV2_HI 41
#define PLL_POSTDIV2_LO 39
#define PLL_POSTDIV1_HI 38
#define PLL_POSTDIV1_LO 36
#define PLL_FRAC_HI 35
#define PLL_FRAC_LO 12
#define PLL_FRAC_BITS (PLL_FRAC_HI - PLL_FRAC_LO + 1)
#define PLL_FBDIV_HI 11
#define PLL_FBDIV_LO 0
#define REG_PLL_CFG 0x1c
#define PLL_CFG_BYPASS BIT(0)
#define PLL_CFG_SRC GENMASK(2, 1)
#define PLL_CFG_OCLK_SEL BIT(3)
#define PLL_CFG_OCLK_GATE BIT(4)
#define PLL_CFG_LOAD_DIVS BIT(5)
#define REG_PLL_LOCK 0x20
/*
* Read-only register indicating the value of the hardware clock source
* override pin. When the first bit of this register is set, PLL clock
* source is forced to the 40M oscillator regardless of PLL_CFG_SRC
* value.
*/
#define REG_PLL_SRC_OVERRIDE 0x24
struct sf_clk_common {
void __iomem *base;
spinlock_t *lock;
struct clk_hw hw;
};
struct sf19a2890_clk {
struct sf_clk_common common;
ulong offset;
};
#define SF_CLK_COMMON(_name, _parents, _op, _flags) \
{ \
.hw.init = CLK_HW_INIT_PARENTS(_name, _parents, _op, _flags), \
}
static inline struct sf_clk_common *hw_to_sf_clk_common(struct clk_hw *hw)
{
return container_of(hw, struct sf_clk_common, hw);
}
static inline struct sf19a2890_clk *cmn_to_clk(struct sf_clk_common *cmn_priv)
{
return container_of(cmn_priv, struct sf19a2890_clk, common);
}
static inline u32 sf_readl(struct sf_clk_common *priv, u32 reg)
{
return readl(priv->base + reg);
}
static inline void sf_writel(struct sf_clk_common *priv, u32 reg, u32 val)
{
return writel(val, priv->base + reg);
}
static inline void sf_rmw(struct sf_clk_common *priv, u32 reg, u32 clr, u32 set)
{
u32 val = sf_readl(priv, reg);
val &= ~clr;
val |= set;
sf_writel(priv, reg, val);
}
static u32 sf_pll_param_get(struct sf19a2890_clk *priv, u32 hi, u32 lo)
{
struct sf_clk_common *cmn = &priv->common;
u32 ret = 0;
int reg_hi = hi / 8;
int reg_lo = lo / 8;
u32 reg_hi_pos = hi % 8;
u32 reg_lo_pos = lo % 8;
int i;
if (reg_hi == reg_lo) {
u32 mask = (BIT(reg_hi_pos - reg_lo_pos + 1)) - 1;
u32 reg_val =
sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_hi));
return (reg_val >> reg_lo_pos) & mask;
}
ret = sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_hi)) &
(BIT(reg_hi_pos + 1) - 1);
for (i = reg_hi - 1; i > reg_lo; i--)
ret = (ret << 8) |
sf_readl(cmn, priv->offset + REG_PLL_PARAM(i));
ret = (ret << (8 - reg_lo_pos)) |
(sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_lo)) >>
reg_lo_pos);
return ret;
}
static unsigned long sf19a28_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
u32 refdiv = sf_pll_param_get(priv, PLL_REFDIV_HI, PLL_REFDIV_LO);
u32 fbdiv = sf_pll_param_get(priv, PLL_FBDIV_HI, PLL_FBDIV_LO);
u32 postdiv1 = sf_pll_param_get(priv, PLL_POSTDIV1_HI, PLL_POSTDIV1_LO);
u32 postdiv2 = sf_pll_param_get(priv, PLL_POSTDIV2_HI, PLL_POSTDIV2_LO);
u32 pll_pd = sf_readl(cmn_priv, PLL_PD);
u32 ref = parent_rate / refdiv;
u32 rate = ref * fbdiv;
u32 frac;
u64 frac_rate;
if (!(pll_pd & PLL_PD_DSM)) {
frac = sf_pll_param_get(priv, PLL_FRAC_HI, PLL_FRAC_LO);
frac_rate = ((u64)rate * frac) >> PLL_FRAC_BITS;
rate += frac_rate;
}
rate = rate / postdiv1 / postdiv2;
return rate;
}
static u8 sf19a28_pll_get_parent(struct clk_hw *hw)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
u32 cfg;
if (sf_readl(cmn_priv, priv->offset + REG_PLL_SRC_OVERRIDE))
return 1;
cfg = sf_readl(cmn_priv, priv->offset + REG_PLL_CFG);
return (FIELD_GET(PLL_CFG_SRC, cfg) == 1);
}
static const struct clk_ops sf19a28_pll_ops = {
.recalc_rate = sf19a28_pll_recalc_rate,
.get_parent = sf19a28_pll_get_parent,
};
static const char * const clk_pll_parents[] = { "osc12m", "osc40m" };
#define SF19A28_PLL(_name, _offset, _flags) \
struct sf19a2890_clk _name = { \
.common = SF_CLK_COMMON(#_name, clk_pll_parents, \
&sf19a28_pll_ops, _flags), \
.offset = REG_PLL_BASE + _offset, \
}
static SF19A28_PLL(pll_cpu, 0x0, 0);
static SF19A28_PLL(pll_ddr, 0x40, 0);
static SF19A28_PLL(pll_cmn, 0x80, 0);
#define REG_MUXDIV_BASE 0x400
#define REG_MUXDIV_CFG 0x0
#define MUXDIV_USE_NCO BIT(3)
#define MUXDIV_SRC_SEL GENMASK(2, 0)
#define REG_MUXDIV_RATIO 0x4
#define MUXDIV_RATIO_MAX 0xff
#define REG_MUXDIV_NCO_V 0x8
#define REG_MUXDIV_EN 0xc
#define REG_MUXDIV_XN_DIV_RATIO 0x10
#define MUXDIV_XN_DIV_MAX 3
static unsigned long sf19a28_muxdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
u32 div;
div = sf_readl(cmn_priv, priv->offset + REG_MUXDIV_RATIO) + 1;
return parent_rate / div;
}
int sf19a28_muxdiv_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
unsigned int div;
div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
if (!div)
div = 1;
else if (div > MUXDIV_RATIO_MAX + 1)
div = MUXDIV_RATIO_MAX + 1;
req->rate = req->best_parent_rate / div;
return 0;
}
static int sf19a28_muxdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
unsigned int div;
div = DIV_ROUND_CLOSEST(parent_rate, rate);
if (div < 1)
div = 1;
else if (div > MUXDIV_RATIO_MAX + 1)
div = MUXDIV_RATIO_MAX + 1;
div -= 1;
sf_writel(cmn_priv, priv->offset + REG_MUXDIV_RATIO, div);
return 0;
}
static int sf19a28_muxdiv_enable(struct clk_hw *hw)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
sf_writel(cmn_priv, priv->offset + REG_MUXDIV_EN, 1);
return 0;
}
static void sf19a28_muxdiv_disable(struct clk_hw *hw)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
sf_writel(cmn_priv, priv->offset + REG_MUXDIV_EN, 0);
}
static int sf19a28_muxdiv_is_enabled(struct clk_hw *hw)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
return !!sf_readl(cmn_priv, priv->offset + REG_MUXDIV_EN);
}
static u8 sf19a28_muxdiv_get_parent(struct clk_hw *hw)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
u32 cfg = sf_readl(cmn_priv, priv->offset + REG_MUXDIV_CFG);
u32 src = FIELD_GET(MUXDIV_SRC_SEL, cfg);
if (src <= 2)
return src;
if (src == 4)
return 3;
return 4;
}
static int sf19a28_muxdiv_set_parent(struct clk_hw *hw, u8 index)
{
struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
u32 src;
if (index <= 2)
src = index;
else if (index == 3)
src = 4;
else
src = 6;
sf_writel(cmn_priv, priv->offset + REG_MUXDIV_CFG, src);
return 0;
}
static const char * const clk_muxdiv_parents[] = { "pll_cpu", "pll_ddr", "pll_cmn",
"osc12m", "osc40m" };
static const struct clk_ops sf19a28_muxdiv_ops = {
.recalc_rate = sf19a28_muxdiv_recalc_rate,
.determine_rate = sf19a28_muxdiv_determine_rate,
.set_rate = sf19a28_muxdiv_set_rate,
.enable = sf19a28_muxdiv_enable,
.disable = sf19a28_muxdiv_disable,
.is_enabled = sf19a28_muxdiv_is_enabled,
.get_parent = sf19a28_muxdiv_get_parent,
.set_parent = sf19a28_muxdiv_set_parent,
};
#define SF19A28_MUXDIV(_name, _offset, _flags) \
struct sf19a2890_clk _name = { \
.common = SF_CLK_COMMON(#_name, clk_muxdiv_parents, \
&sf19a28_muxdiv_ops, _flags), \
.offset = REG_MUXDIV_BASE + _offset, \
}
static SF19A28_MUXDIV(muxdiv_bus1, 0x0, CLK_IS_CRITICAL);
static SF19A28_MUXDIV(muxdiv_bus2, 0x20, CLK_IS_CRITICAL);
static SF19A28_MUXDIV(muxdiv_bus3, 0x40, CLK_IS_CRITICAL);
static SF19A28_MUXDIV(muxdiv_cpu, 0x100, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
static SF19A28_MUXDIV(muxdiv_pbus, 0x120, CLK_IS_CRITICAL);
static SF19A28_MUXDIV(muxdiv_mem_phy, 0x140, CLK_IS_CRITICAL);
static SF19A28_MUXDIV(muxdiv_uart, 0x180, 0);
static SF19A28_MUXDIV(muxdiv_eth_ref, 0x200, 0);
static SF19A28_MUXDIV(muxdiv_eth_byp_ref, 0x220, 0);
static SF19A28_MUXDIV(muxdiv_eth_tsu, 0x240, 0);
static SF19A28_MUXDIV(muxdiv_gmac_byp_ref, 0x260, 0);
static SF19A28_MUXDIV(muxdiv_m6250_0, 0x280, 0);
static SF19A28_MUXDIV(muxdiv_m6250_1, 0x2a0, 0);
static SF19A28_MUXDIV(muxdiv_wlan24_plf, 0x2c0, 0);
static SF19A28_MUXDIV(muxdiv_wlan5_plf, 0x2e0, 0);
static SF19A28_MUXDIV(muxdiv_usbphy_ref, 0x300, 0);
static SF19A28_MUXDIV(muxdiv_tclk, 0x320, 0);
static SF19A28_MUXDIV(muxdiv_npu_pe, 0x340, 0);
static struct clk_hw_onecell_data sf19a2890_hw_clks = {
.num = CLK_SF19A2890_MAX,
.hws = {
[CLK_PLL_CPU] = &pll_cpu.common.hw,
[CLK_PLL_DDR] = &pll_ddr.common.hw,
[CLK_PLL_CMN] = &pll_cmn.common.hw,
[CLK_MUXDIV_BUS1] = &muxdiv_bus1.common.hw,
[CLK_MUXDIV_BUS2] = &muxdiv_bus2.common.hw,
[CLK_MUXDIV_BUS3] = &muxdiv_bus3.common.hw,
[CLK_MUXDIV_CPU] = &muxdiv_cpu.common.hw,
[CLK_MUXDIV_PBUS] = &muxdiv_pbus.common.hw,
[CLK_MUXDIV_MEM_PHY] = &muxdiv_mem_phy.common.hw,
[CLK_MUXDIV_UART] = &muxdiv_uart.common.hw,
[CLK_MUXDIV_ETH_REF] = &muxdiv_eth_ref.common.hw,
[CLK_MUXDIV_ETH_BYP_REF] = &muxdiv_eth_byp_ref.common.hw,
[CLK_MUXDIV_ETH_TSU] = &muxdiv_eth_tsu.common.hw,
[CLK_MUXDIV_GMAC_BYP_REF] = &muxdiv_gmac_byp_ref.common.hw,
[CLK_MUXDIV_M6250_0] = &muxdiv_m6250_0.common.hw,
[CLK_MUXDIV_M6250_1] = &muxdiv_m6250_1.common.hw,
[CLK_MUXDIV_WLAN24_PLF] = &muxdiv_wlan24_plf.common.hw,
[CLK_MUXDIV_WLAN5_PLF] = &muxdiv_wlan5_plf.common.hw,
[CLK_MUXDIV_USBPHY_REF] = &muxdiv_usbphy_ref.common.hw,
[CLK_MUXDIV_TCLK] = &muxdiv_tclk.common.hw,
[CLK_MUXDIV_NPU_PE_CLK] = &muxdiv_npu_pe.common.hw,
},
};
struct sf19a2890_clk_ctrl {
void __iomem *base;
spinlock_t lock;
};
static void __init sf19a2890_clk_init(struct device_node *node)
{
struct sf19a2890_clk_ctrl *ctrl;
int i, ret;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return;
ctrl->base = of_iomap(node, 0);
if (!ctrl->base) {
pr_err("failed to map resources.\n");
return;
}
spin_lock_init(&ctrl->lock);
for (i = 0; i < sf19a2890_hw_clks.num; i++) {
struct clk_hw *hw = sf19a2890_hw_clks.hws[i];
struct sf_clk_common *common;
if (!hw)
continue;
common = hw_to_sf_clk_common(hw);
common->base = ctrl->base;
common->lock = &ctrl->lock;
ret = clk_hw_register(NULL, hw);
if (ret) {
pr_err("Couldn't register clock %d: %d\n", i, ret);
goto err;
}
}
ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, &sf19a2890_hw_clks);
if (ret) {
pr_err("failed to add hw provider.\n");
goto err;
}
return;
err:
iounmap(ctrl->base);
}
CLK_OF_DECLARE(sf19a2890_clk, "siflower,sf19a2890-clk", sf19a2890_clk_init);

View File

@@ -0,0 +1,343 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/pinctrl/consumer.h>
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <asm/div64.h>
#define GPIO_IR(n) (0x40 * (n) + 0x00)
#define GPIO_OR(n) (0x40 * (n) + 0x04)
#define GPIO_OEN(n) (0x40 * (n) + 0x08)
#define GPIO_IMR(n) (0x40 * (n) + 0x0c)
#define GPIO_GPIMR(n) (0x40 * (n) + 0x10)
#define GPIO_PIR(n) (0x40 * (n) + 0x14)
#define GPIO_ITR(n) (0x40 * (n) + 0x18)
#define GPIO_IFR(n) (0x40 * (n) + 0x1c)
#define GPIO_ICR(n) (0x40 * (n) + 0x20)
#define GPIO_GPxIR(n) (0x4 * (n) + 0x4000)
#define GPIOS_PER_GROUP 16
struct sf_gpio_priv {
struct gpio_chip gc;
void __iomem *base;
struct clk *clk;
struct reset_control *rstc;
unsigned int irq[];
};
static u32 sf_gpio_rd(struct sf_gpio_priv *priv, unsigned long reg)
{
return readl_relaxed(priv->base + reg);
}
static void sf_gpio_wr(struct sf_gpio_priv *priv, unsigned long reg,
u32 val)
{
writel_relaxed(val, priv->base + reg);
}
static int sf_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
return sf_gpio_rd(priv, GPIO_IR(offset));
}
static void sf_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
sf_gpio_wr(priv, GPIO_OR(offset), value);
}
static int sf_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
if (sf_gpio_rd(priv, GPIO_OEN(offset)))
return GPIO_LINE_DIRECTION_IN;
else
return GPIO_LINE_DIRECTION_OUT;
}
static int sf_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
sf_gpio_wr(priv, GPIO_OEN(offset), 1);
return 0;
}
static int sf_gpio_direction_output(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
sf_gpio_wr(priv, GPIO_OR(offset), value);
sf_gpio_wr(priv, GPIO_OEN(offset), 0);
return 0;
}
static int sf_gpio_set_debounce(struct gpio_chip *gc, unsigned int offset,
u32 debounce)
{
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned long freq = clk_get_rate(priv->clk);
u64 mul;
/* (ICR + 1) * IFR = debounce_us * clkfreq_mhz / 4 */
mul = (u64)debounce * freq;
do_div(mul, 1000000 * 4);
if (mul > 0xff00)
return -EINVAL;
sf_gpio_wr(priv, GPIO_ICR(offset), 0xff);
sf_gpio_wr(priv, GPIO_IFR(offset), DIV_ROUND_UP(mul, 0x100));
return 0;
}
static int sf_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
{
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_INPUT_DEBOUNCE:
return sf_gpio_set_debounce(gc, offset,
pinconf_to_config_argument(config));
default:
return gpiochip_generic_config(gc, offset, config);
}
}
static void sf_gpio_irq_ack(struct irq_data *data)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned long offset = irqd_to_hwirq(data);
sf_gpio_wr(priv, GPIO_PIR(offset), 0);
}
static void sf_gpio_irq_mask(struct irq_data *data)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned long offset = irqd_to_hwirq(data);
sf_gpio_wr(priv, GPIO_IMR(offset), 1);
sf_gpio_wr(priv, GPIO_GPIMR(offset), 1);
}
static void sf_gpio_irq_unmask(struct irq_data *data)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned long offset = irqd_to_hwirq(data);
sf_gpio_wr(priv, GPIO_IMR(offset), 0);
sf_gpio_wr(priv, GPIO_GPIMR(offset), 0);
}
/* We are actually setting the parents' affinity. */
static int sf_gpio_irq_set_affinity(struct irq_data *data,
const struct cpumask *dest, bool force)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
unsigned long offset = irqd_to_hwirq(data);
const struct cpumask *pdest;
struct irq_desc *pdesc;
struct irq_data *pdata;
unsigned int group;
int ret;
/* Find the parent IRQ and call its irq_set_affinity */
group = offset / GPIOS_PER_GROUP;
if (group >= gc->irq.num_parents)
return -EINVAL;
pdesc = irq_to_desc(gc->irq.parents[group]);
if (!pdesc)
return -EINVAL;
pdata = irq_desc_get_irq_data(pdesc);
if (!pdata->chip->irq_set_affinity)
return -EINVAL;
ret = pdata->chip->irq_set_affinity(pdata, dest, force);
if (ret < 0)
return ret;
/* Copy its effective_affinity back */
pdest = irq_data_get_effective_affinity_mask(pdata);
irq_data_update_effective_affinity(data, pdest);
return ret;
}
static int sf_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned long offset = irqd_to_hwirq(data);
u32 val;
switch (flow_type) {
case IRQ_TYPE_EDGE_RISING:
val = 4;
break;
case IRQ_TYPE_EDGE_FALLING:
val = 2;
break;
case IRQ_TYPE_EDGE_BOTH:
val = 6;
break;
case IRQ_TYPE_LEVEL_HIGH:
val = 1;
break;
case IRQ_TYPE_LEVEL_LOW:
val = 0;
break;
default:
return -EINVAL;
}
sf_gpio_wr(priv, GPIO_ITR(offset), val);
if (flow_type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(data, handle_level_irq);
else
irq_set_handler_locked(data, handle_edge_irq);
return 0;
}
static const struct irq_chip sf_gpio_irqchip = {
.name = KBUILD_MODNAME,
.irq_ack = sf_gpio_irq_ack,
.irq_mask = sf_gpio_irq_mask,
.irq_unmask = sf_gpio_irq_unmask,
.irq_set_affinity = sf_gpio_irq_set_affinity,
.irq_set_type = sf_gpio_irq_set_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void sf_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct irq_chip *ic = irq_desc_get_chip(desc);
struct sf_gpio_priv *priv = gpiochip_get_data(gc);
unsigned int irq = irq_desc_get_irq(desc);
unsigned int group = irq - priv->irq[0];
unsigned long pending;
unsigned int n;
chained_irq_enter(ic, desc);
pending = sf_gpio_rd(priv, GPIO_GPxIR(group));
for_each_set_bit(n, &pending, GPIOS_PER_GROUP) {
generic_handle_domain_irq(gc->irq.domain,
n + group * GPIOS_PER_GROUP);
}
chained_irq_exit(ic, desc);
}
static int sf_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sf_gpio_priv *priv;
struct gpio_irq_chip *girq;
struct gpio_chip *gc;
u32 ngpios, ngroups;
int ret, i;
ngpios = (unsigned int) device_get_match_data(dev);
ngroups = DIV_ROUND_UP(ngpios, GPIOS_PER_GROUP);
priv = devm_kzalloc(dev, struct_size(priv, irq, ngroups), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
if (IS_ERR(priv->rstc))
return PTR_ERR(priv->rstc);
ret = reset_control_deassert(priv->rstc);
if (ret)
return ret;
for (i = 0; i < ngroups; i++) {
ret = platform_get_irq(pdev, i);
if (ret < 0)
return ret;
priv->irq[i] = ret;
}
gc = &priv->gc;
gc->label = KBUILD_MODNAME;
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free;
gc->get_direction = sf_gpio_get_direction;
gc->direction_input = sf_gpio_direction_input;
gc->direction_output = sf_gpio_direction_output;
gc->get = sf_gpio_get_value;
gc->set = sf_gpio_set_value;
gc->set_config = sf_gpio_set_config;
gc->base = -1;
gc->ngpio = ngpios;
girq = &gc->irq;
gpio_irq_chip_set_chip(girq, &sf_gpio_irqchip);
girq->num_parents = ngroups;
girq->parents = priv->irq;
girq->parent_handler = sf_gpio_irq_handler;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
platform_set_drvdata(pdev, priv);
return devm_gpiochip_add_data(dev, gc, priv);
}
static int sf_gpio_remove(struct platform_device *pdev)
{
struct sf_gpio_priv *priv = platform_get_drvdata(pdev);
reset_control_assert(priv->rstc);
return 0;
}
static const struct of_device_id sf_gpio_ids[] = {
{ .compatible = "siflower,sf19a2890-gpio", .data = (void *)49 },
{},
};
MODULE_DEVICE_TABLE(of, sf_gpio_ids);
static struct platform_driver sf_gpio_driver = {
.probe = sf_gpio_probe,
.remove = sf_gpio_remove,
.driver = {
.name = "siflower_gpio",
.of_match_table = sf_gpio_ids,
},
};
module_platform_driver(sf_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Qingfang Deng <qingfang.deng@siflower.com.cn>");
MODULE_DESCRIPTION("GPIO driver for SiFlower SoCs");

View File

@@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Siflower SF19A2890 GMAC glue layer
* SF19A2890 GMAC is a DWMAC 3.73a with a custom HNAT engine
* between its MAC and DMA engine.
*
* Copyright (C) 2024 Chuanhong Guo <gch981213@gmail.com>
*/
#include <linux/of_net.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/nvmem-consumer.h>
#include "stmmac.h"
#include "stmmac_platform.h"
struct sf19a2890_gmac_priv {
struct device *dev;
void __iomem *gmac_cfg;
struct clk *gmac_byp_ref_clk;
};
#define REG_MISC 0x0
#define MISC_PHY_INTF_SEL GENMASK(2, 0)
#define PHY_IF_GMII_MII 0
#define PHY_IF_RGMII 1
#define PHY_IF_RMII 4
#define MISC_PTP_AUX_TS_TRIG BIT(3)
#define MISC_SBD_FLOWCTRL BIT(4)
#define CLK_RMII_OEN BIT(5)
#define REG_CLK_TX_DELAY 0x4
#define REG_CLK_RX_PHY_DELAY 0x8
#define REG_CLK_RX_PHY_DELAY_EN 0xc
/* Siflower stores RGMII delay as a 4-byte hex string in MTD. */
#define SFGMAC_DELAY_STR_LEN 4
static int sfgmac_set_delay_from_nvmem(struct sf19a2890_gmac_priv *priv)
{
struct device_node *np = priv->dev->of_node;
int ret = 0;
struct nvmem_cell *cell;
const void *data;
size_t retlen;
u16 gmac_delay;
u8 delay_tx, delay_rx;
cell = of_nvmem_cell_get(np, "rgmii-delay");
if (IS_ERR(cell))
return PTR_ERR(cell);
data = nvmem_cell_read(cell, &retlen);
nvmem_cell_put(cell);
if (IS_ERR(data))
return PTR_ERR(data);
if (retlen < SFGMAC_DELAY_STR_LEN) {
ret = -EINVAL;
goto exit;
}
ret = kstrtou16(data, 16, &gmac_delay);
if (ret == 0) {
delay_tx = (gmac_delay >> 8) & 0xff;
delay_rx = gmac_delay & 0xff;
writel(delay_tx, priv->gmac_cfg + REG_CLK_TX_DELAY);
writel(delay_rx, priv->gmac_cfg + REG_CLK_RX_PHY_DELAY);
if (delay_rx)
writel(1, priv->gmac_cfg + REG_CLK_RX_PHY_DELAY_EN);
}
exit:
kfree(data);
return ret;
}
static int sfgmac_setup_phy_interface(struct sf19a2890_gmac_priv *priv)
{
phy_interface_t phy_iface;
int mode;
u32 reg;
of_get_phy_mode(priv->dev->of_node, &phy_iface);
switch (phy_iface) {
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_GMII:
mode = PHY_IF_GMII_MII;
break;
case PHY_INTERFACE_MODE_RMII:
mode = PHY_IF_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
mode = PHY_IF_RGMII;
break;
default:
return -EOPNOTSUPP;
}
reg = readl(priv->gmac_cfg + REG_MISC);
reg &= ~MISC_PHY_INTF_SEL;
reg |= FIELD_PREP(MISC_PHY_INTF_SEL, mode);
writel(reg, priv->gmac_cfg + REG_MISC);
return 0;
}
static int sf19a2890_gmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct sf19a2890_gmac_priv *priv;
struct stmmac_resources stmmac_res;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
priv->gmac_byp_ref_clk = devm_clk_get_enabled(&pdev->dev, "gmac_byp_ref");
if (IS_ERR(priv->gmac_byp_ref_clk))
return PTR_ERR(priv->gmac_byp_ref_clk);
priv->gmac_cfg = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(priv->gmac_cfg)) {
dev_err(&pdev->dev, "failed to map regs for gmac config.\n");
return PTR_ERR(priv->gmac_cfg);
}
ret = sfgmac_set_delay_from_nvmem(priv);
if (ret == -EPROBE_DEFER)
return -EPROBE_DEFER;
ret = sfgmac_setup_phy_interface(priv);
if (ret)
return ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
if (IS_ERR(plat_dat)) {
dev_err(&pdev->dev, "dt configuration failed\n");
return PTR_ERR(plat_dat);
}
plat_dat->bsp_priv = priv;
/* This DWMAC has PCSSEL set, but it's not SGMII capable, and doesn't
* return anything in PCS registers under RGMII mode.
* Set this flag to bypass reading pcs regs stmmac_ethtool_get_link_ksettings.
* No idea if it's correct or not.
*/
plat_dat->flags |= STMMAC_FLAG_HAS_INTEGRATED_PCS;
ret = stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
if (ret)
goto err_remove_config_dt;
return 0;
err_remove_config_dt:
if (pdev->dev.of_node)
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
static const struct of_device_id dwmac_sf19a2890_match[] = {
{ .compatible = "siflower,sf19a2890-gmac"},
{ }
};
MODULE_DEVICE_TABLE(of, dwmac_sf19a2890_match);
static struct platform_driver sf19a2890_gmac_driver = {
.probe = sf19a2890_gmac_probe,
.remove_new = stmmac_pltfr_remove,
.driver = {
.name = "sf19a2890-gmac",
.pm = &stmmac_pltfr_pm_ops,
.of_match_table = dwmac_sf19a2890_match,
},
};
module_platform_driver(sf19a2890_gmac_driver);
MODULE_DESCRIPTION("SF19A2890 GMAC driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,6 @@
config PHY_SF19A2890_USB
tristate "SIFLOWER sf19a2890 USB2.0 PHY driver"
default n
select GENERIC_PHY
help
Enable this to support the USB2.0 PHY on the SIFLOWER SF19A2890.

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_PHY_SF19A2890_USB) += phy-sf19a2890-usb.o

View File

@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#define USB_SLEEPM 0x4
struct sf19a2890_usb_phy {
struct device *dev;
struct clk *phy_clk;
struct reset_control *usb_phy_rst;
struct reset_control *power_on_rst;
void __iomem *base;
};
static int sf19a2890_usb_phy_power_on(struct phy *phy)
{
struct sf19a2890_usb_phy *p_phy = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(p_phy->phy_clk);
if (ret < 0) {
dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
return ret;
}
ret = reset_control_deassert(p_phy->usb_phy_rst);
if (ret)
goto err1;
ret = reset_control_deassert(p_phy->power_on_rst);
if (ret)
goto err2;
writel(1, p_phy->base + USB_SLEEPM);
return 0;
err2:
reset_control_assert(p_phy->usb_phy_rst);
err1:
clk_disable_unprepare(p_phy->phy_clk);
return ret;
}
static int sf19a2890_usb_phy_power_off(struct phy *phy)
{
struct sf19a2890_usb_phy *p_phy = phy_get_drvdata(phy);
writel(0, p_phy->base + USB_SLEEPM);
reset_control_assert(p_phy->power_on_rst);
reset_control_assert(p_phy->usb_phy_rst);
clk_disable_unprepare(p_phy->phy_clk);
return 0;
}
static const struct phy_ops sf19a2890_usb_phy_ops = {
.power_on = sf19a2890_usb_phy_power_on,
.power_off = sf19a2890_usb_phy_power_off,
.owner = THIS_MODULE,
};
static int sf19a2890_usb_phy_probe(struct platform_device *pdev)
{
struct sf19a2890_usb_phy *p_phy;
struct phy_provider *provider;
struct phy *phy;
int ret;
p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
if (!p_phy)
return -ENOMEM;
p_phy->dev = &pdev->dev;
p_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(p_phy->base))
return PTR_ERR(p_phy->base);
p_phy->phy_clk = devm_clk_get(p_phy->dev, NULL);
if (IS_ERR(p_phy->phy_clk))
return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->phy_clk),
"failed to get usb phy clock\n");
p_phy->power_on_rst =
devm_reset_control_get_exclusive(&pdev->dev, "power_on_rst");
if (IS_ERR(p_phy->power_on_rst))
return PTR_ERR(p_phy->power_on_rst);
ret = reset_control_assert(p_phy->power_on_rst);
if (ret)
return ret;
p_phy->usb_phy_rst =
devm_reset_control_get_exclusive(&pdev->dev, "usb_phy_rst");
if (IS_ERR(p_phy->usb_phy_rst))
return PTR_ERR(p_phy->usb_phy_rst);
ret = reset_control_assert(p_phy->usb_phy_rst);
if (ret)
return ret;
phy = devm_phy_create(p_phy->dev, NULL, &sf19a2890_usb_phy_ops);
if (IS_ERR(phy))
return dev_err_probe(p_phy->dev, PTR_ERR(phy),
"Failed to create PHY\n");
phy_set_drvdata(phy, p_phy);
provider =
devm_of_phy_provider_register(p_phy->dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return dev_err_probe(p_phy->dev, PTR_ERR(provider),
"Failed to register PHY provider\n");
platform_set_drvdata(pdev, p_phy);
return 0;
}
static const struct of_device_id sf19a2890_usb_phy_of_match[] = {
{
.compatible = "siflower,sf19a2890-usb-phy",
},
{},
};
MODULE_DEVICE_TABLE(of, sf19a2890_usb_phy_of_match);
static struct platform_driver sf19a2890_usb_phy_driver = {
.probe = sf19a2890_usb_phy_probe,
.driver = {
.name = "sf19a2890-usb-phy",
.of_match_table = sf19a2890_usb_phy_of_match,
},
};
module_platform_driver(sf19a2890_usb_phy_driver);
MODULE_AUTHOR("Ziying Wu <ziying.wu@siflower.com.cn>");
MODULE_DESCRIPTION("Siflower SF19A2890 USB2.0 PHY driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,515 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Siflower SF19A2890 pinctrl.
*
* Based on:
* Driver for Broadcom BCM2835 GPIO unit (pinctrl + GPIO)
*
* Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
*/
#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#define MODULE_NAME "sf19a2890-pinctrl"
struct sf_pinctrl {
struct device *dev;
void __iomem *base;
struct pinctrl_dev *pctl_dev;
struct pinctrl_desc pctl_desc;
struct pinctrl_gpio_range gpio_range;
};
#define SF19A28_NUM_GPIOS 49
#define SF19A28_REG_PC(pin) ((pin) * 0x8)
#define PC_OEN BIT(7)
#define PC_ST BIT(6)
#define PC_IE BIT(5)
#define PC_PD BIT(4)
#define PC_PU BIT(3)
#define PC_DS GENMASK(2, 0)
#define DRIVE_MIN 6
#define DRIVE_STEP 3
#define DRIVE_MAX (7 * DRIVE_STEP)
#define SF19A28_REG_PMX(pin) ((pin) * 0x8 + 0x4)
/*
* FUNC_SW:
* 0: Override pad output enable with PC_OEN
* 1: take OEN from GPIO or alternative function
* FMUX_SEL:
* 0: Alternative function mode
* 1: GPIO mode
*/
#define PMX_FUNC_SW BIT(3)
#define PMX_FMUX_SEL BIT(2)
#define PMX_MODE GENMASK(1, 0)
static struct pinctrl_pin_desc sf19a2890_gpio_pins[] = {
PINCTRL_PIN(0, "JTAG_TDO"),
PINCTRL_PIN(1, "JTAG_TDI"),
PINCTRL_PIN(2, "JTAG_TMS"),
PINCTRL_PIN(3, "JTAG_TCK"),
PINCTRL_PIN(4, "JTAG_RST"),
PINCTRL_PIN(5, "SPI_TXD"),
PINCTRL_PIN(6, "SPI_RXD"),
PINCTRL_PIN(7, "SPI_CLK"),
PINCTRL_PIN(8, "SPI_CSN"),
PINCTRL_PIN(9, "UART_TX"),
PINCTRL_PIN(10, "UART_RX"),
PINCTRL_PIN(11, "I2C_DAT"),
PINCTRL_PIN(12, "I2C_CLK"),
PINCTRL_PIN(13, "RGMII_GTX_CLK"),
PINCTRL_PIN(14, "RGMII_TXCLK"),
PINCTRL_PIN(15, "RGMII_TXD0"),
PINCTRL_PIN(16, "RGMII_TXD1"),
PINCTRL_PIN(17, "RGMII_TXD2"),
PINCTRL_PIN(18, "RGMII_TXD3"),
PINCTRL_PIN(19, "RGMII_TXCTL"),
PINCTRL_PIN(20, "RGMII_RXCLK"),
PINCTRL_PIN(21, "RGMII_RXD0"),
PINCTRL_PIN(22, "RGMII_RXD1"),
PINCTRL_PIN(23, "RGMII_RXD2"),
PINCTRL_PIN(24, "RGMII_RXD3"),
PINCTRL_PIN(25, "RGMII_RXCTL"),
PINCTRL_PIN(26, "RGMII_COL"),
PINCTRL_PIN(27, "RGMII_CRS"),
PINCTRL_PIN(28, "RGMII_MDC"),
PINCTRL_PIN(29, "RGMII_MDIO"),
PINCTRL_PIN(30, "HB0_PA_EN"),
PINCTRL_PIN(31, "HB0_LNA_EN"),
PINCTRL_PIN(32, "HB0_SW_CTRL0"),
PINCTRL_PIN(33, "HB0_SW_CTRL1"),
PINCTRL_PIN(34, "HB1_PA_EN"),
PINCTRL_PIN(35, "HB1_LNA_EN"),
PINCTRL_PIN(36, "HB1_SW_CTRL0"),
PINCTRL_PIN(37, "HB1_SW_CTRL1"),
PINCTRL_PIN(38, "LB0_PA_EN"),
PINCTRL_PIN(39, "LB0_LNA_EN"),
PINCTRL_PIN(40, "LB0_SW_CTRL0"),
PINCTRL_PIN(41, "LB0_SW_CTRL1"),
PINCTRL_PIN(42, "LB1_PA_EN"),
PINCTRL_PIN(43, "LB1_LNA_EN"),
PINCTRL_PIN(44, "LB1_SW_CTRL0"),
PINCTRL_PIN(45, "LB1_SW_CTRL1"),
PINCTRL_PIN(46, "CLK_OUT"),
PINCTRL_PIN(47, "EXT_CLK_IN"),
PINCTRL_PIN(48, "DRVVBUS0"),
};
static const char * const sf19a2890_gpio_groups[] = {
"JTAG_TDO",
"JTAG_TDI",
"JTAG_TMS",
"JTAG_TCK",
"JTAG_RST",
"SPI_TXD",
"SPI_RXD",
"SPI_CLK",
"SPI_CSN",
"UART_TX",
"UART_RX",
"I2C_DAT",
"I2C_CLK",
"RGMII_GTX_CLK",
"RGMII_TXCLK",
"RGMII_TXD0",
"RGMII_TXD1",
"RGMII_TXD2",
"RGMII_TXD3",
"RGMII_TXCTL",
"RGMII_RXCLK",
"RGMII_RXD0",
"RGMII_RXD1",
"RGMII_RXD2",
"RGMII_RXD3",
"RGMII_RXCTL",
"RGMII_COL",
"RGMII_CRS",
"RGMII_MDC",
"RGMII_MDIO",
"HB0_PA_EN",
"HB0_LNA_EN",
"HB0_SW_CTRL0",
"HB0_SW_CTRL1",
"HB1_PA_EN",
"HB1_LNA_EN",
"HB1_SW_CTRL0",
"HB1_SW_CTRL1",
"LB0_PA_EN",
"LB0_LNA_EN",
"LB0_SW_CTRL0",
"LB0_SW_CTRL1",
"LB1_PA_EN",
"LB1_LNA_EN",
"LB1_SW_CTRL0",
"LB1_SW_CTRL1",
"CLK_OUT",
"EXT_CLK_IN",
"DRVVBUS0",
};
#define SF19A28_FUNC0 0
#define SF19A28_FUNC1 1
#define SF19A28_FUNC2 2
#define SF19A28_FUNC3 3
#define SF19A28_NUM_FUNCS 4
static const char * const sf19a2890_functions[] = {
"func0", "func1", "func2", "func3"
};
static inline u32 sf_pinctrl_rd(struct sf_pinctrl *pc, ulong reg)
{
return readl(pc->base + reg);
}
static inline void sf_pinctrl_wr(struct sf_pinctrl *pc, ulong reg, u32 val)
{
writel(val, pc->base + reg);
}
static inline void sf_pinctrl_rmw(struct sf_pinctrl *pc, ulong reg, u32 clr,
u32 set)
{
u32 val;
val = sf_pinctrl_rd(pc, reg);
val &= ~clr;
val |= set;
sf_pinctrl_wr(pc, reg, val);
}
static int sf19a2890_pctl_get_groups_count(struct pinctrl_dev *pctldev)
{
return SF19A28_NUM_GPIOS;
}
static const char *sf19a2890_pctl_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return sf19a2890_gpio_groups[selector];
}
static int sf19a2890_pctl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins)
{
*pins = &sf19a2890_gpio_pins[selector].number;
*num_pins = 1;
return 0;
}
static void sf19a2890_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned offset)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
u32 conf = sf_pinctrl_rd(pc, SF19A28_REG_PC(offset));
u32 mux = sf_pinctrl_rd(pc, SF19A28_REG_PMX(offset));
if (!(mux & PMX_FUNC_SW))
seq_puts(s, "Forced OE");
else if (mux & PMX_FMUX_SEL)
seq_puts(s, "GPIO");
else
seq_printf(s, "Func%lu", mux & PMX_MODE);
seq_puts(s, " |");
if (!(conf & PC_OEN) && !(mux & PMX_FUNC_SW))
seq_puts(s, " Output");
if ((conf & PC_ST))
seq_puts(s, " Schmitt_Trigger");
if ((conf & PC_IE))
seq_puts(s, " Input");
if ((conf & PC_PD))
seq_puts(s, " Pull_Down");
if ((conf & PC_PU))
seq_puts(s, " Pull_Up");
seq_printf(s, " Drive: %lu mA",
DRIVE_MIN + (conf & PC_DS) * DRIVE_STEP);
}
static const struct pinctrl_ops sf19a2890_pctl_ops = {
.get_groups_count = sf19a2890_pctl_get_groups_count,
.get_group_name = sf19a2890_pctl_get_group_name,
.get_group_pins = sf19a2890_pctl_get_group_pins,
.pin_dbg_show = sf19a2890_pctl_pin_dbg_show,
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinconf_generic_dt_free_map,
};
static int sf19a2890_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
sf_pinctrl_rmw(pc, SF19A28_REG_PC(offset), PC_IE, PC_OEN);
sf_pinctrl_rmw(pc, SF19A28_REG_PMX(offset), PMX_FUNC_SW, 0);
return 0;
}
static int sf19a2890_pmx_get_functions_count(struct pinctrl_dev *pctldev)
{
return SF19A28_NUM_FUNCS;
}
static const char *sf19a2890_pmx_get_function_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return sf19a2890_functions[selector];
}
static int sf19a2890_pmx_get_function_groups(struct pinctrl_dev *pctldev,
unsigned selector,
const char *const **groups,
unsigned *const num_groups)
{
/* every pin can do every function */
*groups = sf19a2890_gpio_groups;
*num_groups = SF19A28_NUM_GPIOS;
return 0;
}
static int sf19a2890_pmx_set(struct pinctrl_dev *pctldev,
unsigned func_selector, unsigned group_selector)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
unsigned pin = group_selector;
sf_pinctrl_wr(pc, SF19A28_REG_PMX(pin),
PMX_FUNC_SW | FIELD_PREP(PMX_MODE, func_selector));
return 0;
}
static int sf19a2890_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
/* Set to GPIO mode & Let peripheral control OEN */
sf_pinctrl_wr(pc, SF19A28_REG_PMX(offset), PMX_FUNC_SW | PMX_FMUX_SEL);
/*
* Set PC_IE regardless of whether GPIO is in input mode.
* Otherwise GPIO driver can't read back its status in output mode.
*/
sf_pinctrl_rmw(pc, SF19A28_REG_PC(offset), 0, PC_IE);
return 0;
}
static void sf19a2890_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
sf19a2890_pmx_free(pctldev, offset);
}
static const struct pinmux_ops sf19a2890_pmx_ops = {
.free = sf19a2890_pmx_free,
.get_functions_count = sf19a2890_pmx_get_functions_count,
.get_function_name = sf19a2890_pmx_get_function_name,
.get_function_groups = sf19a2890_pmx_get_function_groups,
.set_mux = sf19a2890_pmx_set,
.gpio_request_enable = sf19a2890_pmx_gpio_request_enable,
.gpio_disable_free = sf19a2890_pmx_gpio_disable_free,
};
static int sf19a2890_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *config)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param param = pinconf_to_config_param(*config);
u32 arg = 0;
u32 val = 0;
if (pin >= SF19A28_NUM_GPIOS)
return -EINVAL;
val = sf_pinctrl_rd(pc, SF19A28_REG_PC(pin));
switch (param) {
case PIN_CONFIG_INPUT_SCHMITT:
val &= PC_ST;
if (val)
arg = 1;
break;
case PIN_CONFIG_INPUT_ENABLE:
val &= PC_IE;
if (val)
arg = 1;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
val &= PC_PD;
if (val)
arg = 1;
break;
case PIN_CONFIG_BIAS_PULL_UP:
val &= PC_PU;
if (val)
arg = 1;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
arg = DRIVE_MIN + (val & PC_DS) * DRIVE_STEP;
break;
default:
return -ENOTSUPP;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static int sf19a2890_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *configs, unsigned num_configs)
{
struct sf_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param param;
u32 arg, val;
int i;
val = sf_pinctrl_rd(pc, SF19A28_REG_PC(pin));
if (pin >= SF19A28_NUM_GPIOS)
return -EINVAL;
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
switch (param) {
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
if (arg)
val |= PC_ST;
else
val &= ~PC_ST;
break;
case PIN_CONFIG_INPUT_ENABLE:
if (arg)
val |= PC_IE;
else
val &= ~PC_IE;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
if (arg) {
val |= PC_PD;
val &= ~PC_PU;
} else {
val &= ~PC_PD;
}
break;
case PIN_CONFIG_BIAS_PULL_UP:
if (arg) {
val |= PC_PU;
val &= ~PC_PD;
} else {
val &= ~PC_PU;
}
break;
case PIN_CONFIG_DRIVE_STRENGTH:
val &= ~PC_DS;
if (arg > DRIVE_MAX)
val |= PC_DS;
else if (arg > DRIVE_MIN)
val |= FIELD_PREP(PC_DS, (arg - DRIVE_MIN) /
DRIVE_STEP);
break;
default:
break;
}
sf_pinctrl_wr(pc, SF19A28_REG_PC(pin), val);
}
return 0;
}
static const struct pinconf_ops sf19a2890_pinconf_ops = {
.is_generic = true,
.pin_config_get = sf19a2890_pinconf_get,
.pin_config_set = sf19a2890_pinconf_set,
};
static const struct pinctrl_desc sf19a2890_pinctrl_desc = {
.name = MODULE_NAME,
.pins = sf19a2890_gpio_pins,
.npins = SF19A28_NUM_GPIOS,
.pctlops = &sf19a2890_pctl_ops,
.pmxops = &sf19a2890_pmx_ops,
.confops = &sf19a2890_pinconf_ops,
.owner = THIS_MODULE,
};
static const struct pinctrl_gpio_range sf_pinctrl_gpio_range = {
.name = MODULE_NAME,
.npins = SF19A28_NUM_GPIOS,
};
static const struct of_device_id sf_pinctrl_match[] = {
{ .compatible = "siflower,sf19a2890-pinctrl" },
{}
};
static int sf_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sf_pinctrl *pc;
pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
if (!pc)
return -ENOMEM;
platform_set_drvdata(pdev, pc);
pc->dev = dev;
pc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pc->base))
return PTR_ERR(pc->base);
pc->pctl_desc = sf19a2890_pinctrl_desc;
pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
if (IS_ERR(pc->pctl_dev))
return PTR_ERR(pc->pctl_dev);
return 0;
}
static struct platform_driver sf_pinctrl_driver = {
.probe = sf_pinctrl_probe,
.driver = {
.name = MODULE_NAME,
.of_match_table = sf_pinctrl_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(sf_pinctrl_driver);
MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
MODULE_DESCRIPTION("Siflower SF19A2890 pinctrl driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
struct reset_sf19a28_periph_data {
struct reset_controller_dev rcdev;
void __iomem *base;
spinlock_t lock;
u32 reset_masks[];
};
static inline struct reset_sf19a28_periph_data *
to_reset_sf19a28_periph_data(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct reset_sf19a28_periph_data, rcdev);
}
static int reset_sf19a28_periph_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct reset_sf19a28_periph_data *data = to_reset_sf19a28_periph_data(rcdev);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->base);
if (assert)
reg |= data->reset_masks[id];
else
reg &= ~data->reset_masks[id];
writel(reg, data->base);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int reset_sf19a28_periph_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return reset_sf19a28_periph_update(rcdev, id, true);
}
static int reset_sf19a28_periph_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return reset_sf19a28_periph_update(rcdev, id, false);
}
static int reset_sf19a28_periph_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct reset_sf19a28_periph_data *data = to_reset_sf19a28_periph_data(rcdev);
u32 reg;
reg = readl(data->base);
return !!(reg & data->reset_masks[id]);
}
const struct reset_control_ops reset_sf19a28_periph_ops = {
.assert = reset_sf19a28_periph_assert,
.deassert = reset_sf19a28_periph_deassert,
.status = reset_sf19a28_periph_status,
};
static const struct of_device_id reset_sf19a28_periph_dt_ids[] = {
{ .compatible = "siflower,sf19a2890-periph-reset", },
{ /* sentinel */ },
};
static int reset_sf19a28_periph_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct reset_sf19a28_periph_data *data;
void __iomem *base;
int nr_resets;
int ret, i;
u32 tmp;
nr_resets = of_property_count_u32_elems(node, "siflower,reset-masks");
if (nr_resets < 1) {
ret = of_property_read_u32(node, "siflower,num-resets", &tmp);
if (ret < 0 || tmp < 1)
return -EINVAL;
nr_resets = tmp;
}
if (nr_resets >= 32) {
dev_err(dev, "too many resets.");
return -EINVAL;
}
data = devm_kzalloc(dev, struct_size(data, reset_masks, nr_resets), GFP_KERNEL);
if (!data)
return -ENOMEM;
ret = of_property_read_u32_array(node, "siflower,reset-masks",
data->reset_masks, nr_resets);
if (ret)
for (i = 0; i < nr_resets; i++)
data->reset_masks[i] = BIT(i);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
spin_lock_init(&data->lock);
data->base = base;
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = nr_resets;
data->rcdev.ops = &reset_sf19a28_periph_ops;
data->rcdev.of_node = dev->of_node;
return devm_reset_controller_register(dev, &data->rcdev);
}
static struct platform_driver reset_sf19a28_periph_driver = {
.probe = reset_sf19a28_periph_probe,
.driver = {
.name = "reset-sf19a2890-periph",
.of_match_table = reset_sf19a28_periph_dt_ids,
},
};
builtin_platform_driver(reset_sf19a28_periph_driver);

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
#ifndef __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H
#define __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H
#define CLK_PLL_CPU 0
#define CLK_PLL_DDR 1
#define CLK_PLL_CMN 2
#define CLK_MUXDIV_BUS1 3
#define CLK_MUXDIV_BUS2 4
#define CLK_MUXDIV_BUS3 5
#define CLK_MUXDIV_CPU 6
#define CLK_MUXDIV_PBUS 7
#define CLK_MUXDIV_MEM_PHY 8
#define CLK_MUXDIV_UART 9
#define CLK_MUXDIV_ETH_REF 10
#define CLK_MUXDIV_ETH_BYP_REF 11
#define CLK_MUXDIV_ETH_TSU 12
#define CLK_MUXDIV_GMAC_BYP_REF 13
#define CLK_MUXDIV_M6250_0 14
#define CLK_MUXDIV_M6250_1 15
#define CLK_MUXDIV_WLAN24_PLF 16
#define CLK_MUXDIV_WLAN5_PLF 17
#define CLK_MUXDIV_USBPHY_REF 18
#define CLK_MUXDIV_TCLK 19
#define CLK_MUXDIV_NPU_PE_CLK 20
#define CLK_SF19A2890_MAX 21
#endif /* __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H */