diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/airoha/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/airoha/clk-airoha.c | 454 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 7 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/reset-airoha.c | 173 |
6 files changed, 639 insertions, 0 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fe0e49f6112..8411205ee04 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_$(PHASE_)CLK_GPIO) += clk-gpio.o obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o obj-y += adi/ +obj-y += airoha/ obj-y += analogbits/ obj-y += imx/ obj-$(CONFIG_CLK_JH7110) += starfive/ diff --git a/drivers/clk/airoha/Makefile b/drivers/clk/airoha/Makefile new file mode 100644 index 00000000000..1744c5f7236 --- /dev/null +++ b/drivers/clk/airoha/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# Core +obj-$(CONFIG_ARCH_AIROHA) += clk-airoha.o diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c new file mode 100644 index 00000000000..1b2c4c98de5 --- /dev/null +++ b/drivers/clk/airoha/clk-airoha.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on the Linux clk-en7523.c but majorly reworked + * for U-Boot that doesn't require CCF subsystem. + * + * Major modification, support for set_rate, realtime + * get_rate and split for reset part to a different driver. + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <clk-uclass.h> +#include <dm.h> +#include <dm/devres.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <regmap.h> +#include <syscon.h> + +#include <dt-bindings/clock/en7523-clk.h> + +#define REG_GSW_CLK_DIV_SEL 0x1b4 +#define REG_EMI_CLK_DIV_SEL 0x1b8 +#define REG_BUS_CLK_DIV_SEL 0x1bc +#define REG_SPI_CLK_DIV_SEL 0x1c4 +#define REG_SPI_CLK_FREQ_SEL 0x1c8 +#define REG_NPU_CLK_DIV_SEL 0x1fc + +#define REG_NP_SCU_PCIC 0x88 +#define REG_NP_SCU_SSTR 0x9c +#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) +#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) +#define REG_CRYPTO_CLKSRC2 0x20c + +#define EN7581_MAX_CLKS 9 + +struct airoha_clk_desc { + int id; + const char *name; + u32 base_reg; + u8 base_bits; + u8 base_shift; + union { + const unsigned int *base_values; + unsigned int base_value; + }; + size_t n_base_values; + + u16 div_reg; + u8 div_bits; + u8 div_shift; + u16 div_val0; + u8 div_step; + u8 div_offset; +}; + +struct airoha_clk_priv { + struct regmap *chip_scu_map; + struct airoha_clk_soc_data *data; +}; + +struct airoha_clk_soc_data { + u32 num_clocks; + const struct airoha_clk_desc *descs; +}; + +static const u32 gsw_base[] = { 400000000, 500000000 }; +static const u32 slic_base[] = { 100000000, 3125000 }; + +static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; +static const u32 bus7581_base[] = { 600000000, 540000000 }; +static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; +static const u32 crypto_base[] = { 540000000, 480000000 }; +static const u32 emmc7581_base[] = { 200000000, 150000000 }; + +static const struct airoha_clk_desc en7581_base_clks[EN7581_MAX_CLKS] = { + [EN7523_CLK_GSW] = { + .id = EN7523_CLK_GSW, + .name = "gsw", + + .base_reg = REG_GSW_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = gsw_base, + .n_base_values = ARRAY_SIZE(gsw_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_EMI] = { + .id = EN7523_CLK_EMI, + .name = "emi", + + .base_reg = REG_EMI_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = emi7581_base, + .n_base_values = ARRAY_SIZE(emi7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_BUS] = { + .id = EN7523_CLK_BUS, + .name = "bus", + + .base_reg = REG_BUS_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = bus7581_base, + .n_base_values = ARRAY_SIZE(bus7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_SLIC] = { + .id = EN7523_CLK_SLIC, + .name = "slic", + + .base_reg = REG_SPI_CLK_FREQ_SEL, + .base_bits = 1, + .base_shift = 0, + .base_values = slic_base, + .n_base_values = ARRAY_SIZE(slic_base), + + .div_reg = REG_SPI_CLK_DIV_SEL, + .div_bits = 5, + .div_shift = 24, + .div_val0 = 20, + .div_step = 2, + }, + [EN7523_CLK_SPI] = { + .id = EN7523_CLK_SPI, + .name = "spi", + + .base_reg = REG_SPI_CLK_DIV_SEL, + + .base_value = 400000000, + + .div_bits = 5, + .div_shift = 8, + .div_val0 = 40, + .div_step = 2, + }, + [EN7523_CLK_NPU] = { + .id = EN7523_CLK_NPU, + .name = "npu", + + .base_reg = REG_NPU_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = npu7581_base, + .n_base_values = ARRAY_SIZE(npu7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_CRYPTO] = { + .id = EN7523_CLK_CRYPTO, + .name = "crypto", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 0, + .base_values = crypto_base, + .n_base_values = ARRAY_SIZE(crypto_base), + }, + [EN7581_CLK_EMMC] = { + .id = EN7581_CLK_EMMC, + .name = "emmc", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 12, + .base_values = emmc7581_base, + .n_base_values = ARRAY_SIZE(emmc7581_base), + } +}; + +static u32 airoha_clk_get_base_rate(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->base_bits) + return desc->base_value; + + val >>= desc->base_shift; + val &= (1 << desc->base_bits) - 1; + + if (val >= desc->n_base_values) + return 0; + + return desc->base_values[val]; +} + +static u32 airoha_clk_get_div(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->div_bits) + return 1; + + val >>= desc->div_shift; + val &= (1 << desc->div_bits) - 1; + + if (!val && desc->div_val0) + return desc->div_val0; + + return (val + desc->div_offset) * desc->div_step; +} + +static int airoha_clk_enable(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + int id = clk->id; + + if (id > data->num_clocks) + return -EINVAL; + + return 0; +} + +static int airoha_clk_disable(struct clk *clk) +{ + return 0; +} + +static ulong airoha_clk_get_rate(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int id = clk->id; + u32 reg, val; + ulong rate; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + ret = regmap_read(map, desc->base_reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate = airoha_clk_get_base_rate(desc, val); + + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + ret = regmap_read(map, reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate /= airoha_clk_get_div(desc, val); + + return rate; +} + +static int airoha_clk_search_rate(const struct airoha_clk_desc *desc, int div, + ulong rate) +{ + int i; + + /* Single base rate */ + if (!desc->base_bits) { + if (rate != desc->base_value / div) + goto err; + + return 0; + } + + /* Check every base rate with provided divisor */ + for (i = 0; i < desc->n_base_values; i++) + if (rate == desc->base_values[i] / div) + return i; + +err: + return -EINVAL; +} + +static ulong airoha_clk_set_rate(struct clk *clk, ulong rate) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int div_val, base_val; + u32 reg, val, mask; + int id = clk->id; + int div; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + if (!desc->base_bits && !desc->div_bits) { + dev_err(clk->dev, "Can't set rate for fixed clock %s\n", + desc->name); + return 0; + } + + if (!desc->div_bits) { + /* Divisor not supported, just search in base rate */ + div_val = 0; + base_val = airoha_clk_search_rate(desc, 1, rate); + if (base_val < 0) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } else { + div_val = 0; + + /* Check if div0 satisfy the request */ + if (desc->div_val0) { + base_val = airoha_clk_search_rate(desc, desc->div_val0, + rate); + if (base_val >= 0) { + div_val = 0; + goto apply; + } + + /* Skip checking first divisor val */ + div_val = 1; + } + + /* Simulate rate with every divisor supported */ + for (div_val = div_val + desc->div_offset; + div_val < BIT(desc->div_bits) - 1; div_val++) { + div = div_val * desc->div_step; + + base_val = airoha_clk_search_rate(desc, div, rate); + if (base_val >= 0) + break; + } + + if (div_val == BIT(desc->div_bits) - 1) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } + +apply: + if (desc->div_bits) { + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; + val = div_val << desc->div_shift; + + ret = regmap_update_bits(map, reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update div reg for clock %s\n", + desc->name); + return 0; + } + } + + if (desc->base_bits) { + mask = (BIT(desc->base_bits) - 1) << desc->base_shift; + val = base_val << desc->base_shift; + + ret = regmap_update_bits(map, desc->base_reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update reg for clock %s\n", + desc->name); + return 0; + } + } + + return rate; +} + +const struct clk_ops airoha_clk_ops = { + .enable = airoha_clk_enable, + .disable = airoha_clk_disable, + .get_rate = airoha_clk_get_rate, + .set_rate = airoha_clk_set_rate, +}; + +static int airoha_clk_probe(struct udevice *dev) +{ + struct airoha_clk_priv *priv = dev_get_priv(dev); + ofnode chip_scu_node; + + chip_scu_node = ofnode_by_compatible(ofnode_null(), + "airoha,en7581-chip-scu"); + if (!ofnode_valid(chip_scu_node)) + return -EINVAL; + + priv->chip_scu_map = syscon_node_to_regmap(chip_scu_node); + if (IS_ERR(priv->chip_scu_map)) + return PTR_ERR(priv->chip_scu_map); + + priv->data = (void *)dev_get_driver_data(dev); + + return 0; +} + +static int airoha_clk_bind(struct udevice *dev) +{ + struct udevice *rst_dev; + int ret = 0; + + if (CONFIG_IS_ENABLED(RESET_AIROHA)) { + ret = device_bind_driver_to_node(dev, "airoha-reset", "reset", + dev_ofnode(dev), &rst_dev); + if (ret) + debug("Warning: failed to bind reset controller\n"); + } + + return ret; +} + +static const struct airoha_clk_soc_data en7581_data = { + .num_clocks = ARRAY_SIZE(en7581_base_clks), + .descs = en7581_base_clks, +}; + +static const struct udevice_id airoha_clk_ids[] = { + { .compatible = "airoha,en7581-scu", + .data = (ulong)&en7581_data, + }, + { } +}; + +U_BOOT_DRIVER(airoha_clk) = { + .name = "clk-airoha", + .id = UCLASS_CLK, + .of_match = airoha_clk_ids, + .probe = airoha_clk_probe, + .bind = airoha_clk_bind, + .priv_auto = sizeof(struct airoha_clk_priv), + .ops = &airoha_clk_ops, +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index b408f19a20b..a0d079c4555 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -49,6 +49,13 @@ config TEGRA186_RESET Enable support for manipulating Tegra's on-SoC reset signals via IPC requests to the BPMP (Boot and Power Management Processor). +config RESET_AIROHA + bool "Reset controller driver for Airoha SoCs" + depends on DM_RESET && ARCH_AIROHA + default y + help + Support for reset controller on Airoha SoCs. + config RESET_TI_SCI bool "TI System Control Interface (TI SCI) reset driver" depends on DM_RESET && TI_SCI_PROTOCOL diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 8cab734bdab..b9494396013 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_STI_RESET) += sti-reset.o obj-$(CONFIG_STM32_RESET) += stm32-reset.o obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o +obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o diff --git a/drivers/reset/reset-airoha.c b/drivers/reset/reset-airoha.c new file mode 100644 index 00000000000..e878af6167c --- /dev/null +++ b/drivers/reset/reset-airoha.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on Linux drivers/clk/clk-en7523.c reworked + * and detached to a dedicated driver + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <dm.h> +#include <linux/io.h> +#include <reset-uclass.h> + +#include <dt-bindings/reset/airoha,en7581-reset.h> + +#define RST_NR_PER_BANK 32 + +#define REG_RESET_CONTROL2 0x830 +#define REG_RESET_CONTROL1 0x834 + +struct airoha_reset_priv { + const u16 *bank_ofs; + const u16 *idx_map; + void __iomem *base; +}; + +static const u16 en7581_rst_ofs[] = { + REG_RESET_CONTROL2, + REG_RESET_CONTROL1, +}; + +static const u16 en7581_rst_map[] = { + /* RST_CTRL2 */ + [EN7581_XPON_PHY_RST] = 0, + [EN7581_CPU_TIMER2_RST] = 2, + [EN7581_HSUART_RST] = 3, + [EN7581_UART4_RST] = 4, + [EN7581_UART5_RST] = 5, + [EN7581_I2C2_RST] = 6, + [EN7581_XSI_MAC_RST] = 7, + [EN7581_XSI_PHY_RST] = 8, + [EN7581_NPU_RST] = 9, + [EN7581_I2S_RST] = 10, + [EN7581_TRNG_RST] = 11, + [EN7581_TRNG_MSTART_RST] = 12, + [EN7581_DUAL_HSI0_RST] = 13, + [EN7581_DUAL_HSI1_RST] = 14, + [EN7581_HSI_RST] = 15, + [EN7581_DUAL_HSI0_MAC_RST] = 16, + [EN7581_DUAL_HSI1_MAC_RST] = 17, + [EN7581_HSI_MAC_RST] = 18, + [EN7581_WDMA_RST] = 19, + [EN7581_WOE0_RST] = 20, + [EN7581_WOE1_RST] = 21, + [EN7581_HSDMA_RST] = 22, + [EN7581_TDMA_RST] = 24, + [EN7581_EMMC_RST] = 25, + [EN7581_SOE_RST] = 26, + [EN7581_PCIE2_RST] = 27, + [EN7581_XFP_MAC_RST] = 28, + [EN7581_USB_HOST_P1_RST] = 29, + [EN7581_USB_HOST_P1_U3_PHY_RST] = 30, + /* RST_CTRL1 */ + [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, + [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1, + [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2, + [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, + [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6, + [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8, + [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11, + [EN7581_UART_RST] = RST_NR_PER_BANK + 12, + [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13, + [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14, + [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, + [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, + [EN7581_SFC_RST] = RST_NR_PER_BANK + 18, + [EN7581_UART2_RST] = RST_NR_PER_BANK + 19, + [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20, + [EN7581_FE_RST] = RST_NR_PER_BANK + 21, + [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, + [EN7581_GSW_RST] = RST_NR_PER_BANK + 23, + [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, + [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26, + [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27, + [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, + [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, + [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, +}; + +static int airoha_reset_update(struct airoha_reset_priv *priv, + unsigned long id, bool assert) +{ + void __iomem *addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + u32 val; + + val = readl(addr); + if (assert) + val |= BIT(id % RST_NR_PER_BANK); + else + val &= ~BIT(id % RST_NR_PER_BANK); + writel(val, addr); + + return 0; +} + +static int airoha_reset_assert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, true); +} + +static int airoha_reset_deassert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, false); +} + +static int airoha_reset_status(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + void __iomem *addr; + + addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + + return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); +} + +static int airoha_reset_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + + if (args->args[0] >= ARRAY_SIZE(en7581_rst_map)) + return -EINVAL; + + reset_ctl->id = priv->idx_map[args->args[0]]; + + return 0; +} + +static struct reset_ops airoha_reset_ops = { + .of_xlate = airoha_reset_xlate, + .rst_assert = airoha_reset_assert, + .rst_deassert = airoha_reset_deassert, + .rst_status = airoha_reset_status, +}; + +static int airoha_reset_probe(struct udevice *dev) +{ + struct airoha_reset_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -ENOMEM; + + priv->bank_ofs = en7581_rst_ofs; + priv->idx_map = en7581_rst_map; + + return 0; +} + +U_BOOT_DRIVER(airoha_reset) = { + .name = "airoha-reset", + .id = UCLASS_RESET, + .probe = airoha_reset_probe, + .ops = &airoha_reset_ops, + .priv_auto = sizeof(struct airoha_reset_priv), +}; |