diff options
author | Tom Rini <trini@konsulko.com> | 2023-10-16 09:09:54 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-10-16 09:09:54 -0400 |
commit | 2e1577e8360d8e467bf28009f6763a2ac511f743 (patch) | |
tree | cf05b376e7b46e4b5b4b61d04f6976de85ab31d4 /drivers | |
parent | 3c3f1626919cd93cbe6c56e3849937de5be18dbb (diff) | |
parent | 7cd53e0d5203f8e25bb69d2e675769888fcbc754 (diff) |
Merge tag 'u-boot-amlogic-20231015' of https://source.denx.de/u-boot/custodians/u-boot-amlogic
- add Amlogic A1 clock driver
- add Amlogic A1 reset support
- add USB Device support for Amlogic A1
- enable RNG on Amlogic A1 & Amlogic S4
- move Amlogic Secure Monitor to standalone driver
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/meson/Kconfig | 8 | ||||
-rw-r--r-- | drivers/clk/meson/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/meson/a1.c | 729 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/phy/meson-g12a-usb2.c | 235 | ||||
-rw-r--r-- | drivers/reset/reset-meson.c | 42 | ||||
-rw-r--r-- | drivers/rng/meson-rng.c | 72 | ||||
-rw-r--r-- | drivers/sm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/sm/Makefile | 5 | ||||
-rw-r--r-- | drivers/sm/meson-sm.c | 198 | ||||
-rw-r--r-- | drivers/sm/sandbox-sm.c | 76 | ||||
-rw-r--r-- | drivers/sm/sm-uclass.c | 55 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-meson-g12a.c | 79 |
15 files changed, 1479 insertions, 35 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index a25f6ae02f..a073230c26 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -118,6 +118,8 @@ source "drivers/scsi/Kconfig" source "drivers/serial/Kconfig" +source "drivers/sm/Kconfig" + source "drivers/smem/Kconfig" source "drivers/sound/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index efc2a4afb2..74f940a57d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/ obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/ obj-$(CONFIG_$(SPL_)SYSINFO) += sysinfo/ +obj-$(CONFIG_$(SPL_TPL_)SM) += sm/ obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/ obj-$(CONFIG_$(SPL_)NVME) += nvme/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 994b44ad7a..cdc9d6f76c 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -21,3 +21,11 @@ config CLK_MESON_G12A help Enable clock support for the Amlogic G12A SoC family, such as the S905X/D2 + +config CLK_MESON_A1 + bool "Enable clock support for Amlogic A1" + depends on CLK && ARCH_MESON + default MESON_A1 + help + Enable clock support for the Amlogic A1 SoC family, such as + the A113L diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index a486b13e9c..d975f07aab 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CLK_MESON_AXG) += axg.o obj-$(CONFIG_CLK_MESON_AXG) += axg-ao.o obj-$(CONFIG_CLK_MESON_G12A) += g12a.o obj-$(CONFIG_CLK_MESON_G12A) += g12a-ao.o +obj-$(CONFIG_CLK_MESON_A1) += a1.o diff --git a/drivers/clk/meson/a1.c b/drivers/clk/meson/a1.c new file mode 100644 index 0000000000..1075ba7333 --- /dev/null +++ b/drivers/clk/meson/a1.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 SberDevices, Inc. + * Author: Igor Prusov <ivprusov@salutedevices.com> + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <regmap.h> +#include <asm/arch/clock-a1.h> +#include <dt-bindings/clock/amlogic,a1-pll-clkc.h> +#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h> +#include "clk_meson.h" + +/* + * This driver supports both PLL and peripherals clock sources. + * Following operations are supported: + * - calculating clock frequency on a limited tree + * - reading muxes and dividers + * - enabling/disabling gates without propagation + * - reparenting without rate propagation, only on muxes + * - setting rates with limited reparenting, only on dividers with mux parent + */ + +#define NR_CLKS 154 +#define NR_PLL_CLKS 11 + +/* External clock IDs. Those should not overlap with regular IDs */ +#define EXTERNAL_XTAL (NR_CLKS + 0) +#define EXTERNAL_FCLK_DIV2 (NR_CLKS + 1) +#define EXTERNAL_FCLK_DIV3 (NR_CLKS + 2) +#define EXTERNAL_FCLK_DIV5 (NR_CLKS + 3) +#define EXTERNAL_FCLK_DIV7 (NR_CLKS + 4) + +#define EXTERNAL_FIXPLL_IN (NR_PLL_CLKS + 1) + +#define SET_PARM_VALUE(_priv, _parm, _val) \ + regmap_update_bits((_priv)->map, (_parm)->reg_off, \ + SETPMASK((_parm)->width, (_parm)->shift), \ + (_val) << (_parm)->shift) + +#define GET_PARM_VALUE(_priv, _parm) \ +({ \ + uint _reg; \ + regmap_read((_priv)->map, (_parm)->reg_off, &_reg); \ + PARM_GET((_parm)->width, (_parm)->shift, _reg); \ +}) + +struct meson_clk { + struct regmap *map; +}; + +/** + * enum meson_clk_type - The type of clock + * @MESON_CLK_ANY: Special value that matches any clock type + * @MESON_CLK_GATE: This clock is a gate + * @MESON_CLK_MUX: This clock is a multiplexer + * @MESON_CLK_DIV: This clock is a configurable divider + * @MESON_CLK_FIXED_DIV: This clock is a configurable divider + * @MESON_CLK_EXTERNAL: This is an external clock from different clock provider + * @MESON_CLK_PLL: This is a PLL + */ +enum meson_clk_type { + MESON_CLK_ANY = 0, + MESON_CLK_GATE, + MESON_CLK_MUX, + MESON_CLK_DIV, + MESON_CLK_FIXED_DIV, + MESON_CLK_EXTERNAL, + MESON_CLK_PLL, +}; + +/** + * struct meson_clk_info - The parameters defining a clock + * @name: Name of the clock + * @parm: Register bits description for muxes and dividers + * @div: Fixed divider value + * @parents: List of parent clock IDs + * @type: Clock type + */ +struct meson_clk_info { + const char *name; + union { + const struct parm *parm; + u8 div; + }; + const unsigned int *parents; + const enum meson_clk_type type; +}; + +/** + * struct meson_clk_data - Clocks supported by clock provider + * @num_clocks: Number of clocks + * @clocks: Array of clock descriptions + * + */ +struct meson_clk_data { + const u8 num_clocks; + const struct meson_clk_info **clocks; +}; + +/* Clock description initialization macros */ + +/* A multiplexer */ +#define CLK_MUX(_name, _reg, _shift, _width, ...) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[])__VA_ARGS__, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + }, \ + .name = (_name), \ + .type = MESON_CLK_MUX, \ + }) + +/* A divider with an integral divisor */ +#define CLK_DIV(_name, _reg, _shift, _width, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + }, \ + .name = (_name), \ + .type = MESON_CLK_DIV, \ + }) + +/* A fixed divider */ +#define CLK_DIV_FIXED(_name, _div, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .div = (_div), \ + .name = (_name), \ + .type = MESON_CLK_FIXED_DIV, \ + }) + +/* An external clock */ +#define CLK_EXTERNAL(_name) \ + (&(struct meson_clk_info){ \ + .name = (_name), \ + .parents = (const unsigned int[]) { -ENOENT }, \ + .type = MESON_CLK_EXTERNAL, \ + }) + +/* A clock gate */ +#define CLK_GATE(_name, _reg, _shift, _parent) \ + (&(struct meson_clk_info){ \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = &(struct parm) { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = 1, \ + }, \ + .name = (_name), \ + .type = MESON_CLK_GATE, \ + }) + +/* A PLL clock */ +#define CLK_PLL(_name, _parent, ...) \ + (&(struct meson_clk_info){ \ + .name = (_name), \ + .parents = (const unsigned int[]) { (_parent) }, \ + .parm = (const struct parm[])__VA_ARGS__, \ + .type = MESON_CLK_PLL, \ + }) + +/* A1 peripherals clocks */ +static const struct meson_clk_info *meson_clocks[] = { + [CLKID_SPIFC_SEL] = CLK_MUX("spifc_sel", A1_SPIFC_CLK_CTRL, 9, 2, { + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + }), + [CLKID_SPIFC_SEL2] = CLK_MUX("spifc_sel2", A1_SPIFC_CLK_CTRL, 15, 1, { + CLKID_SPIFC_DIV, + EXTERNAL_XTAL, + }), + [CLKID_USB_BUS_SEL] = CLK_MUX("usb_bus_sel", A1_USB_BUSCLK_CTRL, 9, 2, { + -ENOENT, + CLKID_SYS, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + }), + [CLKID_SYS] = CLK_MUX("sys", A1_SYS_CLK_CTRL0, 31, 1, { + CLKID_SYS_A, + CLKID_SYS_B, + }), + [CLKID_SYS_A_SEL] = CLK_MUX("sys_a_sel", A1_SYS_CLK_CTRL0, 10, 3, { + -ENOENT, + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + -ENOENT, + -ENOENT, + -ENOENT, + }), + [CLKID_SYS_B_SEL] = CLK_MUX("sys_b_sel", A1_SYS_CLK_CTRL0, 26, 3, { + -ENOENT, + EXTERNAL_FCLK_DIV2, + EXTERNAL_FCLK_DIV3, + EXTERNAL_FCLK_DIV5, + -ENOENT, + -ENOENT, + -ENOENT, + -ENOENT, + }), + + [CLKID_SPIFC_DIV] = CLK_DIV("spifc_div", A1_SPIFC_CLK_CTRL, 0, 8, + CLKID_SPIFC_SEL + ), + [CLKID_USB_BUS_DIV] = CLK_DIV("usb_bus_div", A1_USB_BUSCLK_CTRL, 0, 8, + CLKID_USB_BUS_SEL + ), + [CLKID_SYS_A_DIV] = CLK_DIV("sys_a_div", A1_SYS_CLK_CTRL0, 0, 10, + CLKID_SYS_A_SEL + ), + [CLKID_SYS_B_DIV] = CLK_DIV("sys_b_div", A1_SYS_CLK_CTRL0, 16, 10, + CLKID_SYS_B_SEL + ), + + [CLKID_SPIFC] = CLK_GATE("spifc", A1_SPIFC_CLK_CTRL, 8, + CLKID_SPIFC_SEL2 + ), + [CLKID_USB_BUS] = CLK_GATE("usb_bus", A1_USB_BUSCLK_CTRL, 8, + CLKID_USB_BUS_DIV + ), + [CLKID_SYS_A] = CLK_GATE("sys_a", A1_SYS_CLK_CTRL0, 13, + CLKID_SYS_A_DIV + ), + [CLKID_SYS_B] = CLK_GATE("sys_b", A1_SYS_CLK_CTRL0, 29, + CLKID_SYS_B_DIV + ), + [CLKID_FIXPLL_IN] = CLK_GATE("fixpll_in", A1_SYS_OSCIN_CTRL, 1, + EXTERNAL_XTAL + ), + [CLKID_USB_PHY_IN] = CLK_GATE("usb_phy_in", A1_SYS_OSCIN_CTRL, 2, + EXTERNAL_XTAL + ), + [CLKID_USB_PHY] = CLK_GATE("usb_phy", A1_SYS_CLK_EN0, 27, + CLKID_SYS + ), + [CLKID_SARADC] = CLK_GATE("saradc", A1_SAR_ADC_CLK_CTR, 8, + -ENOENT + ), + [CLKID_SARADC_EN] = CLK_GATE("saradc_en", A1_SYS_CLK_EN0, 13, + CLKID_SYS + ), + + [EXTERNAL_XTAL] = CLK_EXTERNAL("xtal"), + [EXTERNAL_FCLK_DIV2] = CLK_EXTERNAL("fclk_div2"), + [EXTERNAL_FCLK_DIV3] = CLK_EXTERNAL("fclk_div3"), + [EXTERNAL_FCLK_DIV5] = CLK_EXTERNAL("fclk_div5"), + [EXTERNAL_FCLK_DIV7] = CLK_EXTERNAL("fclk_div7"), +}; + +/* A1 PLL clocks */ +static const struct meson_clk_info *meson_pll_clocks[] = { + [EXTERNAL_FIXPLL_IN] = CLK_EXTERNAL("fixpll_in"), + + [CLKID_FIXED_PLL_DCO] = CLK_PLL("fixed_pll_dco", EXTERNAL_FIXPLL_IN, { + {A1_ANACTRL_FIXPLL_CTRL0, 0, 8}, + {A1_ANACTRL_FIXPLL_CTRL0, 10, 5}, + }), + + [CLKID_FCLK_DIV2_DIV] = CLK_DIV_FIXED("fclk_div2_div", 2, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV3_DIV] = CLK_DIV_FIXED("fclk_div3_div", 3, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV5_DIV] = CLK_DIV_FIXED("fclk_div5_div", 5, + CLKID_FIXED_PLL + ), + [CLKID_FCLK_DIV7_DIV] = CLK_DIV_FIXED("fclk_div7_div", 7, + CLKID_FIXED_PLL + ), + + [CLKID_FIXED_PLL] = CLK_GATE("fixed_pll", A1_ANACTRL_FIXPLL_CTRL0, 20, + CLKID_FIXED_PLL_DCO + ), + [CLKID_FCLK_DIV2] = CLK_GATE("fclk_div2", A1_ANACTRL_FIXPLL_CTRL0, 21, + CLKID_FCLK_DIV2_DIV + ), + [CLKID_FCLK_DIV3] = CLK_GATE("fclk_div3", A1_ANACTRL_FIXPLL_CTRL0, 22, + CLKID_FCLK_DIV3_DIV + ), + [CLKID_FCLK_DIV5] = CLK_GATE("fclk_div5", A1_ANACTRL_FIXPLL_CTRL0, 23, + CLKID_FCLK_DIV5_DIV + ), + [CLKID_FCLK_DIV7] = CLK_GATE("fclk_div7", A1_ANACTRL_FIXPLL_CTRL0, 24, + CLKID_FCLK_DIV7_DIV + ), +}; + +static const struct meson_clk_info *meson_clk_get_info(struct clk *clk, ulong id, + enum meson_clk_type type) +{ + struct meson_clk_data *data; + const struct meson_clk_info *info; + + data = (struct meson_clk_data *)dev_get_driver_data(clk->dev); + if (id >= data->num_clocks) + return ERR_PTR(-EINVAL); + + info = data->clocks[id]; + if (!info) + return ERR_PTR(-ENOENT); + + if (type != MESON_CLK_ANY && type != info->type) + return ERR_PTR(-EINVAL); + + return info; +} + +static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id); + +static int meson_set_gate(struct clk *clk, bool on) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + debug("%s: %sabling %lu\n", __func__, on ? "en" : "dis", clk->id); + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + SET_PARM_VALUE(priv, info->parm, on); + + return 0; +} + +static int meson_clk_enable(struct clk *clk) +{ + return meson_set_gate(clk, true); +} + +static int meson_clk_disable(struct clk *clk) +{ + return meson_set_gate(clk, false); +} + +static ulong meson_div_get_rate(struct clk *clk, unsigned long id) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + u16 n; + ulong rate; + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_DIV); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* Actual divider value is (field value + 1), hence the increment */ + n = GET_PARM_VALUE(priv, info->parm) + 1; + + rate = meson_clk_get_rate_by_id(clk, info->parents[0]); + + return rate / n; +} + +static int meson_clk_get_parent(struct clk *clk, unsigned long id) +{ + uint reg = 0; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* For muxes we read currently selected parent from register, + * for other types there is always only one element in parents array. + */ + if (info->type == MESON_CLK_MUX) { + reg = GET_PARM_VALUE(priv, info->parm); + if (IS_ERR_VALUE(reg)) + return reg; + } + + return info->parents[reg]; +} + +static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) +{ + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + const struct parm *pm, *pn; + ulong parent_rate_mhz; + unsigned int parent; + u16 n, m; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + pm = &info->parm[0]; + pn = &info->parm[1]; + + n = GET_PARM_VALUE(priv, pn); + m = GET_PARM_VALUE(priv, pm); + + if (n == 0) + return -EINVAL; + + parent = info->parents[0]; + parent_rate_mhz = meson_clk_get_rate_by_id(clk, parent) / 1000000; + + return parent_rate_mhz * m / n * 1000000; +} + +static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) +{ + ulong rate, parent; + const struct meson_clk_info *info; + + if (IS_ERR_VALUE(id)) + return id; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + if (IS_ERR(info)) + return PTR_ERR(info); + + switch (info->type) { + case MESON_CLK_PLL: + rate = meson_pll_get_rate(clk, id); + break; + case MESON_CLK_GATE: + case MESON_CLK_MUX: + parent = meson_clk_get_parent(clk, id); + rate = meson_clk_get_rate_by_id(clk, parent); + break; + case MESON_CLK_DIV: + rate = meson_div_get_rate(clk, id); + break; + case MESON_CLK_FIXED_DIV: + parent = meson_clk_get_parent(clk, id); + rate = meson_clk_get_rate_by_id(clk, parent) / info->div; + break; + case MESON_CLK_EXTERNAL: { + int ret; + struct clk external_clk; + + ret = clk_get_by_name(clk->dev, info->name, &external_clk); + if (ret) + return ret; + + rate = clk_get_rate(&external_clk); + break; + } + default: + rate = -EINVAL; + break; + } + + return rate; +} + +static ulong meson_clk_get_rate(struct clk *clk) +{ + return meson_clk_get_rate_by_id(clk, clk->id); +} + +/* This implements rate propagation for dividers placed after multiplexer: + * ---------|\ + * ..... | |---DIV-- + * ---------|/ + */ +static ulong meson_composite_set_rate(struct clk *clk, ulong id, ulong rate) +{ + unsigned int i, best_div_val; + unsigned long best_delta, best_parent; + const struct meson_clk_info *div; + const struct meson_clk_info *mux; + struct meson_clk *priv = dev_get_priv(clk->dev); + + div = meson_clk_get_info(clk, id, MESON_CLK_DIV); + if (IS_ERR(div)) + return PTR_ERR(div); + + mux = meson_clk_get_info(clk, div->parents[0], MESON_CLK_MUX); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + best_parent = -EINVAL; + best_delta = ULONG_MAX; + for (i = 0; i < (1 << mux->parm->width); i++) { + unsigned long parent_rate, delta; + unsigned int div_val; + + parent_rate = meson_clk_get_rate_by_id(clk, mux->parents[i]); + if (IS_ERR_VALUE(parent_rate)) + continue; + + /* If overflow, try to use max divider value */ + div_val = min(DIV_ROUND_CLOSEST(parent_rate, rate), + (1UL << div->parm->width)); + + delta = abs(rate - (parent_rate / div_val)); + if (delta < best_delta) { + best_delta = delta; + best_div_val = div_val; + best_parent = i; + } + } + + if (IS_ERR_VALUE(best_parent)) + return best_parent; + + SET_PARM_VALUE(priv, mux->parm, best_parent); + /* Divider is set to (field value + 1), hence the decrement */ + SET_PARM_VALUE(priv, div->parm, best_div_val - 1); + + return 0; +} + +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate); + +static ulong meson_mux_set_rate(struct clk *clk, unsigned long id, ulong rate) +{ + int i; + ulong ret = -EINVAL; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_MUX); + if (IS_ERR(info)) + return PTR_ERR(info); + + for (i = 0; i < (1 << info->parm->width); i++) { + ret = meson_clk_set_rate_by_id(clk, info->parents[i], rate); + if (!ret) { + SET_PARM_VALUE(priv, info->parm, i); + break; + } + } + + return ret; +} + +/* Rate propagation is implemented for a subcection of a clock tree, that is + * required at boot stage. + */ +static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned int id, ulong rate) +{ + switch (id) { + case CLKID_SPIFC_DIV: + case CLKID_USB_BUS_DIV: + return meson_composite_set_rate(clk, id, rate); + case CLKID_SPIFC: + case CLKID_USB_BUS: { + unsigned long parent = meson_clk_get_parent(clk, id); + + return meson_clk_set_rate_by_id(clk, parent, rate); + } + case CLKID_SPIFC_SEL2: + return meson_mux_set_rate(clk, id, rate); + } + + return -EINVAL; +} + +static ulong meson_clk_set_rate(struct clk *clk, ulong rate) +{ + return meson_clk_set_rate_by_id(clk, clk->id, rate); +} + +static int meson_mux_set_parent_by_id(struct clk *clk, unsigned int parent_id) +{ + unsigned int i, parent_index; + struct meson_clk *priv = dev_get_priv(clk->dev); + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_MUX); + if (IS_ERR(info)) + return PTR_ERR(info); + + parent_index = -EINVAL; + for (i = 0; i < (1 << info->parm->width); i++) { + if (parent_id == info->parents[i]) { + parent_index = i; + break; + } + } + + if (IS_ERR_VALUE(parent_index)) + return parent_index; + + SET_PARM_VALUE(priv, info->parm, parent_index); + + return 0; +} + +static int meson_clk_set_parent(struct clk *clk, struct clk *parent_clk) +{ + return meson_mux_set_parent_by_id(clk, parent_clk->id); +} + +static struct clk_ops meson_clk_ops = { + .disable = meson_clk_disable, + .enable = meson_clk_enable, + .get_rate = meson_clk_get_rate, + .set_rate = meson_clk_set_rate, + .set_parent = meson_clk_set_parent, +}; + +static int meson_clk_probe(struct udevice *dev) +{ + struct meson_clk *priv = dev_get_priv(dev); + + return regmap_init_mem(dev_ofnode(dev), &priv->map); +} + +struct meson_clk_data meson_a1_peripherals_info = { + .clocks = meson_clocks, + .num_clocks = ARRAY_SIZE(meson_clocks), +}; + +struct meson_clk_data meson_a1_pll_info = { + .clocks = meson_pll_clocks, + .num_clocks = ARRAY_SIZE(meson_pll_clocks), +}; + +static const struct udevice_id meson_clk_ids[] = { + { + .compatible = "amlogic,a1-peripherals-clkc", + .data = (ulong)&meson_a1_peripherals_info, + }, + { + .compatible = "amlogic,a1-pll-clkc", + .data = (ulong)&meson_a1_pll_info, + }, + { } +}; + +U_BOOT_DRIVER(meson_clk) = { + .name = "meson-clk-a1", + .id = UCLASS_CLK, + .of_match = meson_clk_ids, + .priv_auto = sizeof(struct meson_clk), + .ops = &meson_clk_ops, + .probe = meson_clk_probe, +}; + +static const char *meson_clk_get_name(struct clk *clk, int id) +{ + const struct meson_clk_info *info; + + info = meson_clk_get_info(clk, id, MESON_CLK_ANY); + + return IS_ERR(info) ? "unknown" : info->name; +} + +static int meson_clk_dump(struct clk *clk) +{ + const struct meson_clk_info *info; + struct meson_clk *priv; + unsigned long rate; + char *state, frequency[80]; + int parent; + + priv = dev_get_priv(clk->dev); + + info = meson_clk_get_info(clk, clk->id, MESON_CLK_ANY); + if (IS_ERR(info) || !info->name) + return -EINVAL; + + rate = clk_get_rate(clk); + if (IS_ERR_VALUE(rate)) + sprintf(frequency, "unknown"); + else + sprintf(frequency, "%lu", rate); + + if (info->type == MESON_CLK_GATE) + state = GET_PARM_VALUE(priv, info->parm) ? "enabled" : "disabled"; + else + state = "N/A"; + + parent = meson_clk_get_parent(clk, clk->id); + printf("%15s%20s%20s%15s\n", + info->name, + frequency, + meson_clk_get_name(clk, parent), + state); + + return 0; +} + +static int meson_clk_dump_dev(struct udevice *dev) +{ + int i; + struct meson_clk_data *data; + const char *sep = "--------------------"; + + printf("%s:\n", dev->name); + printf("%.15s%s%s%.15s\n", sep, sep, sep, sep); + printf("%15s%20s%20s%15s\n", "clk", "frequency", "parent", "state"); + printf("%.15s%s%s%.15s\n", sep, sep, sep, sep); + + data = (struct meson_clk_data *)dev_get_driver_data(dev); + for (i = 0; i < data->num_clocks; i++) { + meson_clk_dump(&(struct clk){ + .dev = dev, + .id = i + }); + } + + return 0; +} + +int soc_clk_dump(void) +{ + struct udevice *dev; + int i = 0; + + while (!uclass_get_device(UCLASS_CLK, i++, &dev)) { + if (dev->driver == DM_DRIVER_GET(meson_clk)) { + meson_clk_dump_dev(dev); + printf("\n"); + } + } + + return 0; +} diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 8ac5769ed9..60138beca4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -200,7 +200,7 @@ config MESON_GXL_USB_PHY config MESON_G12A_USB_PHY bool "Amlogic Meson G12A USB PHYs" - depends on PHY && ARCH_MESON && MESON_G12A + depends on PHY && ARCH_MESON && (MESON_G12A || MESON_A1) imply REGMAP help This is the generic phy driver for the Amlogic Meson G12A diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c index 8b24322515..4ba3992bda 100644 --- a/drivers/phy/meson-g12a-usb2.c +++ b/drivers/phy/meson-g12a-usb2.c @@ -19,17 +19,34 @@ #include <linux/delay.h> #include <linux/printk.h> #include <power/regulator.h> +#include <power-domain.h> #include <reset.h> #include <clk.h> #include <linux/bitops.h> #include <linux/compat.h> +#include <linux/bitfield.h> #define PHY_CTRL_R0 0x0 #define PHY_CTRL_R1 0x4 #define PHY_CTRL_R2 0x8 + #define PHY_CTRL_R3 0xc + #define PHY_CTRL_R3_SQUELCH_REF GENMASK(1, 0) + #define PHY_CTRL_R3_HSDIC_REF GENMASK(3, 2) + #define PHY_CTRL_R3_DISC_THRESH GENMASK(7, 4) + #define PHY_CTRL_R4 0x10 + #define PHY_CTRL_R4_CALIB_CODE_7_0 GENMASK(7, 0) + #define PHY_CTRL_R4_CALIB_CODE_15_8 GENMASK(15, 8) + #define PHY_CTRL_R4_CALIB_CODE_23_16 GENMASK(23, 16) + #define PHY_CTRL_R4_I_C2L_CAL_EN BIT(24) + #define PHY_CTRL_R4_I_C2L_CAL_RESET_N BIT(25) + #define PHY_CTRL_R4_I_C2L_CAL_DONE BIT(26) + #define PHY_CTRL_R4_TEST_BYPASS_MODE_EN BIT(27) + #define PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0 GENMASK(29, 28) + #define PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2 GENMASK(31, 30) + #define PHY_CTRL_R5 0x14 #define PHY_CTRL_R6 0x18 #define PHY_CTRL_R7 0x1c @@ -38,35 +55,131 @@ #define PHY_CTRL_R10 0x28 #define PHY_CTRL_R11 0x2c #define PHY_CTRL_R12 0x30 + #define PHY_CTRL_R13 0x34 + #define PHY_CTRL_R13_CUSTOM_PATTERN_19 GENMASK(7, 0) + #define PHY_CTRL_R13_LOAD_STAT BIT(14) + #define PHY_CTRL_R13_UPDATE_PMA_SIGNALS BIT(15) + #define PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET GENMASK(20, 16) + #define PHY_CTRL_R13_CLEAR_HOLD_HS_DISCONNECT BIT(21) + #define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_VAL BIT(22) + #define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_EN BIT(23) + #define PHY_CTRL_R13_I_C2L_HS_EN BIT(24) + #define PHY_CTRL_R13_I_C2L_FS_EN BIT(25) + #define PHY_CTRL_R13_I_C2L_LS_EN BIT(26) + #define PHY_CTRL_R13_I_C2L_HS_OE BIT(27) + #define PHY_CTRL_R13_I_C2L_FS_OE BIT(28) + #define PHY_CTRL_R13_I_C2L_HS_RX_EN BIT(29) + #define PHY_CTRL_R13_I_C2L_FSLS_RX_EN BIT(30) + #define PHY_CTRL_R14 0x38 #define PHY_CTRL_R15 0x3c + #define PHY_CTRL_R16 0x40 + #define PHY_CTRL_R16_MPLL_M GENMASK(8, 0) + #define PHY_CTRL_R16_MPLL_N GENMASK(14, 10) + #define PHY_CTRL_R16_MPLL_TDC_MODE BIT(20) + #define PHY_CTRL_R16_MPLL_SDM_EN BIT(21) + #define PHY_CTRL_R16_MPLL_LOAD BIT(22) + #define PHY_CTRL_R16_MPLL_DCO_SDM_EN BIT(23) + #define PHY_CTRL_R16_MPLL_LOCK_LONG GENMASK(25, 24) + #define PHY_CTRL_R16_MPLL_LOCK_F BIT(26) + #define PHY_CTRL_R16_MPLL_FAST_LOCK BIT(27) + #define PHY_CTRL_R16_MPLL_EN BIT(28) + #define PHY_CTRL_R16_MPLL_RESET BIT(29) + #define PHY_CTRL_R16_MPLL_LOCK BIT(30) + #define PHY_CTRL_R16_MPLL_LOCK_DIG BIT(31) + #define PHY_CTRL_R17 0x44 + #define PHY_CTRL_R17_MPLL_FRAC_IN GENMASK(13, 0) + #define PHY_CTRL_R17_MPLL_FIX_EN BIT(16) + #define PHY_CTRL_R17_MPLL_LAMBDA1 GENMASK(19, 17) + #define PHY_CTRL_R17_MPLL_LAMBDA0 GENMASK(22, 20) + #define PHY_CTRL_R17_MPLL_FILTER_MODE BIT(23) + #define PHY_CTRL_R17_MPLL_FILTER_PVT2 GENMASK(27, 24) + #define PHY_CTRL_R17_MPLL_FILTER_PVT1 GENMASK(31, 28) + #define PHY_CTRL_R18 0x48 + #define PHY_CTRL_R18_MPLL_LKW_SEL GENMASK(1, 0) + #define PHY_CTRL_R18_MPLL_LK_W GENMASK(5, 2) + #define PHY_CTRL_R18_MPLL_LK_S GENMASK(11, 6) + #define PHY_CTRL_R18_MPLL_DCO_M_EN BIT(12) + #define PHY_CTRL_R18_MPLL_DCO_CLK_SEL BIT(13) + #define PHY_CTRL_R18_MPLL_PFD_GAIN GENMASK(15, 14) + #define PHY_CTRL_R18_MPLL_ROU GENMASK(18, 16) + #define PHY_CTRL_R18_MPLL_DATA_SEL GENMASK(21, 19) + #define PHY_CTRL_R18_MPLL_BIAS_ADJ GENMASK(23, 22) + #define PHY_CTRL_R18_MPLL_BB_MODE GENMASK(25, 24) + #define PHY_CTRL_R18_MPLL_ALPHA GENMASK(28, 26) + #define PHY_CTRL_R18_MPLL_ADJ_LDO GENMASK(30, 29) + #define PHY_CTRL_R18_MPLL_ACG_RANGE BIT(31) + #define PHY_CTRL_R19 0x4c + #define PHY_CTRL_R20 0x50 + #define PHY_CTRL_R20_USB2_IDDET_EN BIT(0) + #define PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0 GENMASK(3, 1) + #define PHY_CTRL_R20_USB2_OTG_VBUSDET_EN BIT(4) + #define PHY_CTRL_R20_USB2_AMON_EN BIT(5) + #define PHY_CTRL_R20_USB2_CAL_CODE_R5 BIT(6) + #define PHY_CTRL_R20_BYPASS_OTG_DET BIT(7) + #define PHY_CTRL_R20_USB2_DMON_EN BIT(8) + #define PHY_CTRL_R20_USB2_DMON_SEL_3_0 GENMASK(12, 9) + #define PHY_CTRL_R20_USB2_EDGE_DRV_EN BIT(13) + #define PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0 GENMASK(15, 14) + #define PHY_CTRL_R20_USB2_BGR_ADJ_4_0 GENMASK(20, 16) + #define PHY_CTRL_R20_USB2_BGR_START BIT(21) + #define PHY_CTRL_R20_USB2_BGR_VREF_4_0 GENMASK(28, 24) + #define PHY_CTRL_R20_USB2_BGR_DBG_1_0 GENMASK(30, 29) + #define PHY_CTRL_R20_BYPASS_CAL_DONE_R5 BIT(31) + #define PHY_CTRL_R21 0x54 + #define PHY_CTRL_R21_USB2_BGR_FORCE BIT(0) + #define PHY_CTRL_R21_USB2_CAL_ACK_EN BIT(1) + #define PHY_CTRL_R21_USB2_OTG_ACA_EN BIT(2) + #define PHY_CTRL_R21_USB2_TX_STRG_PD BIT(3) + #define PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0 GENMASK(5, 4) + #define PHY_CTRL_R21_BYPASS_UTMI_CNTR GENMASK(15, 6) + #define PHY_CTRL_R21_BYPASS_UTMI_REG GENMASK(25, 20) + #define PHY_CTRL_R22 0x58 #define PHY_CTRL_R23 0x5c #define RESET_COMPLETE_TIME 1000 #define PLL_RESET_COMPLETE_TIME 100 +enum meson_soc_id { + MESON_SOC_A1, + MESON_SOC_G12A, +}; + struct phy_meson_g12a_usb2_priv { struct regmap *regmap; #if CONFIG_IS_ENABLED(CLK) struct clk clk; #endif struct reset_ctl reset; +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + struct power_domain pwrdm; +#endif + int soc_id; }; static int phy_meson_g12a_usb2_init(struct phy *phy) { struct udevice *dev = phy->dev; struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); + u32 value; int ret; +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + return ret; + } +#endif + ret = reset_assert(&priv->reset); udelay(1); ret |= reset_deassert(&priv->reset); @@ -79,25 +192,91 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0); /* PLL Setup : 24MHz * 20 / 1 = 480MHz */ - regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414); - regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000); - regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5); + regmap_write(priv->regmap, PHY_CTRL_R16, + FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) | + FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) | + PHY_CTRL_R16_MPLL_LOAD | + FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) | + PHY_CTRL_R16_MPLL_FAST_LOCK | + PHY_CTRL_R16_MPLL_EN | + PHY_CTRL_R16_MPLL_RESET); + + regmap_write(priv->regmap, PHY_CTRL_R17, + FIELD_PREP(PHY_CTRL_R17_MPLL_FRAC_IN, 0) | + FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA1, 7) | + FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA0, 7) | + FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) | + FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9)); + + value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) | + FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) | + FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) | + PHY_CTRL_R18_MPLL_ACG_RANGE; + + if (priv->soc_id == MESON_SOC_A1) + value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL; + + regmap_write(priv->regmap, PHY_CTRL_R18, value); udelay(PLL_RESET_COMPLETE_TIME); /* UnReset PLL */ - regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414); + regmap_write(priv->regmap, PHY_CTRL_R16, + FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) | + FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) | + PHY_CTRL_R16_MPLL_LOAD | + FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) | + PHY_CTRL_R16_MPLL_FAST_LOCK | + PHY_CTRL_R16_MPLL_EN); /* PHY Tuning */ - regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18); - regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff); + regmap_write(priv->regmap, PHY_CTRL_R20, + FIELD_PREP(PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0, 4) | + PHY_CTRL_R20_USB2_OTG_VBUSDET_EN | + FIELD_PREP(PHY_CTRL_R20_USB2_DMON_SEL_3_0, 15) | + PHY_CTRL_R20_USB2_EDGE_DRV_EN | + FIELD_PREP(PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0, 3) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_ADJ_4_0, 0) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) | + FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0)); + + if (priv->soc_id == MESON_SOC_G12A) + regmap_write(priv->regmap, PHY_CTRL_R4, + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) | + PHY_CTRL_R4_TEST_BYPASS_MODE_EN | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0)); + else if (priv->soc_id == MESON_SOC_A1) + regmap_write(priv->regmap, PHY_CTRL_R21, + PHY_CTRL_R21_USB2_CAL_ACK_EN | + PHY_CTRL_R21_USB2_TX_STRG_PD | + FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2)); /* Tuning Disconnect Threshold */ - regmap_write(priv->regmap, PHY_CTRL_R3, 0x34); + regmap_write(priv->regmap, PHY_CTRL_R3, + FIELD_PREP(PHY_CTRL_R3_SQUELCH_REF, 0) | + FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) | + FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3)); /* Analog Settings */ - regmap_write(priv->regmap, PHY_CTRL_R14, 0); - regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000); + if (priv->soc_id == MESON_SOC_G12A) { + regmap_write(priv->regmap, PHY_CTRL_R14, 0); + regmap_write(priv->regmap, PHY_CTRL_R13, + PHY_CTRL_R13_UPDATE_PMA_SIGNALS | + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } else if (priv->soc_id == MESON_SOC_A1) { + regmap_write(priv->regmap, PHY_CTRL_R13, + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } return 0; } @@ -108,6 +287,10 @@ static int phy_meson_g12a_usb2_exit(struct phy *phy) struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); int ret; +#if CONFIG_IS_ENABLED(CLK) + clk_disable(&priv->clk); +#endif + ret = reset_assert(&priv->reset); if (ret) return ret; @@ -125,6 +308,8 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev) struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev); int ret; + priv->soc_id = (enum meson_soc_id)dev_get_driver_data(dev); + ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); if (ret) return ret; @@ -141,24 +326,40 @@ int meson_g12a_usb2_phy_probe(struct udevice *dev) return ret; } +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + ret = power_domain_get(dev, &priv->pwrdm); + if (ret < 0 && ret != -ENODEV) { + pr_err("failed to get power domain\n"); + return ret; + } + + if (ret != -ENODEV) { + ret = power_domain_on(&priv->pwrdm); + if (ret < 0) { + pr_err("failed to enable power domain\n"); + return ret; + } + } +#endif + #if CONFIG_IS_ENABLED(CLK) ret = clk_get_by_index(dev, 0, &priv->clk); if (ret < 0) return ret; - - ret = clk_enable(&priv->clk); - if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { - pr_err("failed to enable PHY clock\n"); - clk_free(&priv->clk); - return ret; - } #endif return 0; } static const struct udevice_id meson_g12a_usb2_phy_ids[] = { - { .compatible = "amlogic,g12a-usb2-phy" }, + { + .compatible = "amlogic,g12a-usb2-phy", + .data = (ulong)MESON_SOC_G12A, + }, + { + .compatible = "amlogic,a1-usb2-phy", + .data = (ulong)MESON_SOC_A1, + }, { } }; diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 64bc696f13..9d0c8b354f 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -13,18 +13,26 @@ #include <reset-uclass.h> #include <regmap.h> #include <linux/bitops.h> +#include <linux/delay.h> -#define REG_COUNT 8 #define BITS_PER_REG 32 -#define LEVEL_OFFSET 0x7c + +struct meson_reset_drvdata { + unsigned int reg_count; + unsigned int level_offset; +}; struct meson_reset_priv { struct regmap *regmap; + struct meson_reset_drvdata *drvdata; }; static int meson_reset_request(struct reset_ctl *reset_ctl) { - if (reset_ctl->id > (REG_COUNT * BITS_PER_REG)) + struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); + struct meson_reset_drvdata *data = priv->drvdata; + + if (reset_ctl->id > (data->reg_count * BITS_PER_REG)) return -EINVAL; return 0; @@ -33,9 +41,10 @@ static int meson_reset_request(struct reset_ctl *reset_ctl) static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert) { struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); + struct meson_reset_drvdata *data = priv->drvdata; uint bank = reset_ctl->id / BITS_PER_REG; uint offset = reset_ctl->id % BITS_PER_REG; - uint reg_offset = LEVEL_OFFSET + (bank << 2); + uint reg_offset = data->level_offset + (bank << 2); uint val; regmap_read(priv->regmap, reg_offset, &val); @@ -64,15 +73,36 @@ struct reset_ops meson_reset_ops = { .rst_deassert = meson_reset_deassert, }; +static const struct meson_reset_drvdata meson_gxbb_data = { + .reg_count = 8, + .level_offset = 0x7c, +}; + +static const struct meson_reset_drvdata meson_a1_data = { + .reg_count = 3, + .level_offset = 0x40, +}; + static const struct udevice_id meson_reset_ids[] = { - { .compatible = "amlogic,meson-gxbb-reset" }, - { .compatible = "amlogic,meson-axg-reset" }, + { + .compatible = "amlogic,meson-gxbb-reset", + .data = (ulong)&meson_gxbb_data, + }, + { + .compatible = "amlogic,meson-axg-reset", + .data = (ulong)&meson_gxbb_data, + }, + { + .compatible = "amlogic,meson-a1-reset", + .data = (ulong)&meson_a1_data, + }, { } }; static int meson_reset_probe(struct udevice *dev) { struct meson_reset_priv *priv = dev_get_priv(dev); + priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev); return regmap_init_mem(dev_ofnode(dev), &priv->regmap); } diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c index e0a1e8c7e0..fd2988e91b 100644 --- a/drivers/rng/meson-rng.c +++ b/drivers/rng/meson-rng.c @@ -10,10 +10,23 @@ #include <dm.h> #include <rng.h> #include <asm/io.h> +#include <linux/iopoll.h> + +#define RNG_DATA 0x00 +#define RNG_S4_DATA 0x08 +#define RNG_S4_CFG 0x00 + +#define RUN_BIT BIT(0) +#define SEED_READY_STS_BIT BIT(31) + +struct meson_rng_priv { + u32 (*read)(fdt_addr_t base); +}; struct meson_rng_plat { fdt_addr_t base; struct clk clk; + struct meson_rng_priv *priv; }; /** @@ -27,10 +40,11 @@ struct meson_rng_plat { static int meson_rng_read(struct udevice *dev, void *data, size_t len) { struct meson_rng_plat *pdata = dev_get_plat(dev); + struct meson_rng_priv *priv = pdata->priv; char *buffer = (char *)data; while (len) { - u32 rand = readl(pdata->base); + u32 rand = priv->read(pdata->base); size_t step; if (len >= 4) @@ -44,6 +58,47 @@ static int meson_rng_read(struct udevice *dev, void *data, size_t len) return 0; } +static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) +{ + u32 status = 0; + int ret; + + ret = readl_relaxed_poll_timeout(cfg_addr, + status, !(status & bit), + 10000); + if (ret) + return -EBUSY; + + return 0; +} + +static u32 meson_common_rng_read(fdt_addr_t base) +{ + return readl(base); +} + +static u32 meson_s4_rng_read(fdt_addr_t base) +{ + void __iomem *cfg_addr = (void *)base + RNG_S4_CFG; + int err; + + writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); + + err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); + if (err) { + pr_err("Seed isn't ready, try again\n"); + return err; + } + + err = meson_rng_wait_status(cfg_addr, RUN_BIT); + if (err) { + pr_err("Can't get random number, try again\n"); + return err; + } + + return readl_relaxed(base + RNG_S4_DATA); +} + /** * meson_rng_probe() - probe rng device * @@ -59,6 +114,8 @@ static int meson_rng_probe(struct udevice *dev) if (err) return err; + pdata->priv = (struct meson_rng_priv *)dev_get_driver_data(dev); + return 0; } @@ -102,9 +159,22 @@ static const struct dm_rng_ops meson_rng_ops = { .read = meson_rng_read, }; +static const struct meson_rng_priv meson_rng_priv = { + .read = meson_common_rng_read, +}; + +static const struct meson_rng_priv meson_rng_priv_s4 = { + .read = meson_s4_rng_read, +}; + static const struct udevice_id meson_rng_match[] = { { .compatible = "amlogic,meson-rng", + .data = (ulong)&meson_rng_priv, + }, + { + .compatible = "amlogic,meson-s4-rng", + .data = (ulong)&meson_rng_priv_s4, }, {}, }; diff --git a/drivers/sm/Kconfig b/drivers/sm/Kconfig new file mode 100644 index 0000000000..f0987275d2 --- /dev/null +++ b/drivers/sm/Kconfig @@ -0,0 +1,9 @@ +config SM + bool "Enable Secure Monitor driver support" + +config MESON_SM + bool "Amlogic Secure Monitor driver" + select SM + default n + help + Say y here to enable the Amlogic secure monitor driver. diff --git a/drivers/sm/Makefile b/drivers/sm/Makefile new file mode 100644 index 0000000000..da81ee898a --- /dev/null +++ b/drivers/sm/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += sm-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox-sm.o +obj-$(CONFIG_MESON_SM) += meson-sm.o diff --git a/drivers/sm/meson-sm.c b/drivers/sm/meson-sm.c new file mode 100644 index 0000000000..25adaf4560 --- /dev/null +++ b/drivers/sm/meson-sm.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <dm.h> +#include <regmap.h> +#include <sm.h> +#include <sm-uclass.h> +#include <stdlib.h> +#include <syscon.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <meson/sm.h> +#include <linux/bitfield.h> +#include <linux/err.h> +#include <linux/sizes.h> + +struct meson_sm_cmd { + u32 smc_id; +}; + +#define SET_CMD(index, id) \ + [index] = { \ + .smc_id = (id), \ + } + +struct meson_sm_data { + u32 cmd_get_shmem_in; + u32 cmd_get_shmem_out; + unsigned int shmem_size; + struct meson_sm_cmd cmd[]; +}; + +struct meson_sm_priv { + void *sm_shmem_in; + void *sm_shmem_out; + const struct meson_sm_data *data; +}; + +static unsigned long __meson_sm_call(u32 cmd, const struct pt_regs *args) +{ + struct pt_regs r = *args; + + r.regs[0] = cmd; + smc_call(&r); + + return r.regs[0]; +}; + +static u32 meson_sm_get_cmd(const struct meson_sm_data *data, + u32 cmd_index) +{ + struct meson_sm_cmd cmd; + + if (cmd_index >= MESON_SMC_CMD_COUNT) + return 0; + + cmd = data->cmd[cmd_index]; + return cmd.smc_id; +} + +static int meson_sm_call(struct udevice *dev, u32 cmd_index, s32 *retval, + struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + u32 cmd, ret; + + cmd = meson_sm_get_cmd(priv->data, cmd_index); + if (!cmd) + return -ENOENT; + + ret = __meson_sm_call(cmd, args); + if (retval) + *retval = ret; + + return 0; +} + +static int meson_sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + s32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + ret = meson_sm_call(dev, cmd_index, &nbytes, args); + if (ret) + return ret; + + if (nbytes < 0 || nbytes > size) + return -ENOBUFS; + + /* In some cases (for example GET_CHIP_ID command), + * SMC doesn't return the number of bytes read, even + * though the bytes were actually read into sm_shmem_out. + * So this check is needed. + */ + ret = nbytes; + if (!nbytes) + nbytes = size; + + memcpy(buffer, priv->sm_shmem_out, nbytes); + + return ret; +} + +static int meson_sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + s32 nbytes; + int ret; + + if (!buffer || size > priv->data->shmem_size) + return -EINVAL; + + memcpy(priv->sm_shmem_in, buffer, size); + + ret = meson_sm_call(dev, cmd_index, &nbytes, args); + if (ret) + return ret; + + if (nbytes <= 0 || nbytes > size) + return -EIO; + + return nbytes; +} + +static int meson_sm_probe(struct udevice *dev) +{ + struct meson_sm_priv *priv = dev_get_priv(dev); + struct pt_regs regs = { 0 }; + + priv->data = (struct meson_sm_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + priv->sm_shmem_in = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_in, ®s); + + if (!priv->sm_shmem_in) + return -ENOMEM; + + priv->sm_shmem_out = + (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, ®s); + + if (!priv->sm_shmem_out) + return -ENOMEM; + + pr_debug("meson sm driver probed\n" + "shmem_in addr: 0x%p, shmem_out addr: 0x%p\n", + priv->sm_shmem_in, + priv->sm_shmem_out); + + return 0; +} + +static const struct meson_sm_data meson_sm_gxbb_data = { + .cmd_get_shmem_in = 0x82000020, + .cmd_get_shmem_out = 0x82000021, + .shmem_size = SZ_4K, + .cmd = { + SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030), + SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031), + SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044), + SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093), + }, +}; + +static const struct udevice_id meson_sm_ids[] = { + { + .compatible = "amlogic,meson-gxbb-sm", + .data = (ulong)&meson_sm_gxbb_data, + }, + { } +}; + +static const struct sm_ops sm_ops = { + .sm_call = meson_sm_call, + .sm_call_read = meson_sm_call_read, + .sm_call_write = meson_sm_call_write, +}; + +U_BOOT_DRIVER(meson_sm) = { + .name = "meson_sm", + .id = UCLASS_SM, + .of_match = meson_sm_ids, + .probe = meson_sm_probe, + .priv_auto = sizeof(struct meson_sm_priv), + .ops = &sm_ops, +}; diff --git a/drivers/sm/sandbox-sm.c b/drivers/sm/sandbox-sm.c new file mode 100644 index 0000000000..109ddb2af5 --- /dev/null +++ b/drivers/sm/sandbox-sm.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <sm.h> +#include <sm-uclass.h> +#include <sandbox-sm.h> +#include <asm/ptrace.h> +#include <dm/device.h> +#include <linux/sizes.h> + +static u8 test_buffer[SZ_4K]; + +static int sandbox_sm_call(struct udevice *dev, u32 cmd_index, s32 *smc_ret, + struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT) + return -EINVAL; + + if (smc_ret) + *smc_ret = 0; + + return 0; +} + +static int sandbox_sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer) + return -EINVAL; + + if (size > sizeof(test_buffer)) + return -EINVAL; + + memcpy(buffer, test_buffer, size); + + return size; +} + +static int sandbox_sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd_index, struct pt_regs *args) +{ + if (cmd_index >= SANDBOX_SMC_CMD_COUNT || !buffer) + return -EINVAL; + + if (size > sizeof(test_buffer)) + return -EINVAL; + + memcpy(test_buffer, buffer, size); + + return size; +} + +static const struct udevice_id sandbox_sm_ids[] = { + { + .compatible = "sandbox,sm", + }, + {}, +}; + +static const struct sm_ops sandbox_sm_ops = { + .sm_call = sandbox_sm_call, + .sm_call_read = sandbox_sm_call_read, + .sm_call_write = sandbox_sm_call_write, +}; + +U_BOOT_DRIVER(sm) = { + .name = "sm", + .id = UCLASS_SM, + .of_match = sandbox_sm_ids, + .ops = &sandbox_sm_ops, +}; diff --git a/drivers/sm/sm-uclass.c b/drivers/sm/sm-uclass.c new file mode 100644 index 0000000000..6a8b702629 --- /dev/null +++ b/drivers/sm/sm-uclass.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SberDevices, Inc. + * + * Author: Alexey Romanov <avromanov@salutedevices.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <sm-uclass.h> + +static const struct sm_ops *get_sm_ops(struct udevice *dev) +{ + return (const struct sm_ops *)dev->driver->ops; +} + +int sm_call(struct udevice *dev, u32 cmd, s32 *ret, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call) + return ops->sm_call(dev, cmd, ret, args); + + return -ENOSYS; +} + +int sm_call_read(struct udevice *dev, void *buffer, size_t size, + u32 cmd, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call_read) + return ops->sm_call_read(dev, buffer, size, cmd, + args); + + return -ENOSYS; +} + +int sm_call_write(struct udevice *dev, void *buffer, size_t size, + u32 cmd, struct pt_regs *args) +{ + const struct sm_ops *ops = get_sm_ops(dev); + + if (ops->sm_call_write) + return ops->sm_call_write(dev, buffer, size, cmd, + args); + + return -ENOSYS; +} + +UCLASS_DRIVER(sm) = { + .name = "sm", + .id = UCLASS_SM, +}; diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index dc5a976f71..e0356e653f 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -29,6 +29,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/compat.h> +#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h> /* USB2 Ports Control Registers */ @@ -103,10 +104,22 @@ enum { PHY_COUNT, }; -static const char *phy_names[PHY_COUNT] = { +static const char *const dwc3_meson_g12a_phy_names[] = { "usb2-phy0", "usb2-phy1", "usb3-phy0", }; +static const char *const dwc3_meson_a1_phy_names[] = { + "usb2-phy0", "usb2-phy1" +}; + +struct dwc3_meson_g12a; + +struct dwc3_meson_g12a_drvdata { + const char *const *phy_names; + unsigned int phy_cnt; + int (*clk_init)(struct dwc3_meson_g12a *priv); +}; + struct dwc3_meson_g12a { struct udevice *dev; struct regmap *regmap; @@ -120,6 +133,7 @@ struct dwc3_meson_g12a { #if CONFIG_IS_ENABLED(DM_REGULATOR) struct udevice *vbus_supply; #endif + struct dwc3_meson_g12a_drvdata *drvdata; }; #define U2P_REG_SIZE 0x20 @@ -294,10 +308,11 @@ int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode) static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) { + struct dwc3_meson_g12a_drvdata *data = priv->drvdata; int i, ret; - for (i = 0 ; i < PHY_COUNT ; ++i) { - ret = generic_phy_get_by_name(priv->dev, phy_names[i], + for (i = 0 ; i < data->phy_cnt; ++i) { + ret = generic_phy_get_by_name(priv->dev, data->phy_names[i], &priv->phys[i]); if (ret == -ENOENT || ret == -ENODATA) continue; @@ -355,18 +370,36 @@ static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv) return 0; } +static int dwc3_meson_a1_clk_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = clk_get_by_name(priv->dev, "usb_bus", &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret) + return ret; + + return 0; +} + static int dwc3_meson_g12a_probe(struct udevice *dev) { struct dwc3_meson_g12a *priv = dev_get_plat(dev); + struct dwc3_meson_g12a_drvdata *data = + (struct dwc3_meson_g12a_drvdata *)dev_get_driver_data(dev); int ret, i; + priv->drvdata = data; priv->dev = dev; ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); if (ret) return ret; - ret = dwc3_meson_g12a_clk_init(priv); + ret = data->clk_init(priv); if (ret) return ret; @@ -399,7 +432,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) if (ret) return ret; - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -408,7 +441,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) goto err_phy_init; } - for (i = 0; i < PHY_COUNT; ++i) { + for (i = 0; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -420,7 +453,7 @@ static int dwc3_meson_g12a_probe(struct udevice *dev) return 0; err_phy_init: - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt ; ++i) { if (!priv->phys[i].dev) continue; @@ -433,20 +466,21 @@ err_phy_init: static int dwc3_meson_g12a_remove(struct udevice *dev) { struct dwc3_meson_g12a *priv = dev_get_plat(dev); + struct dwc3_meson_g12a_drvdata *data = priv->drvdata; int i; reset_release_all(&priv->reset, 1); clk_release_all(&priv->clk, 1); - for (i = 0; i < PHY_COUNT; ++i) { + for (i = 0; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; generic_phy_power_off(&priv->phys[i]); } - for (i = 0 ; i < PHY_COUNT ; ++i) { + for (i = 0 ; i < data->phy_cnt; ++i) { if (!priv->phys[i].dev) continue; @@ -456,11 +490,26 @@ static int dwc3_meson_g12a_remove(struct udevice *dev) return dm_scan_fdt_dev(dev); } +static const struct dwc3_meson_g12a_drvdata meson_g12a_drvdata = { + .phy_names = dwc3_meson_g12a_phy_names, + .phy_cnt = ARRAY_SIZE(dwc3_meson_g12a_phy_names), + .clk_init = dwc3_meson_g12a_clk_init, +}; + +static const struct dwc3_meson_g12a_drvdata meson_a1_drvdata = { + .phy_names = dwc3_meson_a1_phy_names, + .phy_cnt = ARRAY_SIZE(dwc3_meson_a1_phy_names), + .clk_init = dwc3_meson_a1_clk_init, +}; + static int dwc3_meson_g12a_child_pre_probe(struct udevice *dev) { if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb")) return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL); + if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb")) + return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_PERIPHERAL); + return 0; } @@ -469,11 +518,21 @@ static int dwc3_meson_g12a_child_post_remove(struct udevice *dev) if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-g12a-usb")) return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST); + if (ofnode_device_is_compatible(dev_ofnode(dev), "amlogic,meson-a1-usb")) + return dwc3_meson_g12a_force_mode(dev->parent, USB_DR_MODE_HOST); + return 0; } static const struct udevice_id dwc3_meson_g12a_ids[] = { - { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { + .compatible = "amlogic,meson-g12a-usb-ctrl", + .data = (ulong)&meson_g12a_drvdata, + }, + { + .compatible = "amlogic,meson-a1-usb-ctrl", + .data = (ulong)&meson_a1_drvdata, + }, { } }; |