aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-10-16 09:09:54 -0400
committerTom Rini <trini@konsulko.com>2023-10-16 09:09:54 -0400
commit2e1577e8360d8e467bf28009f6763a2ac511f743 (patch)
treecf05b376e7b46e4b5b4b61d04f6976de85ab31d4 /drivers
parent3c3f1626919cd93cbe6c56e3849937de5be18dbb (diff)
parent7cd53e0d5203f8e25bb69d2e675769888fcbc754 (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/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/clk/meson/Kconfig8
-rw-r--r--drivers/clk/meson/Makefile1
-rw-r--r--drivers/clk/meson/a1.c729
-rw-r--r--drivers/phy/Kconfig2
-rw-r--r--drivers/phy/meson-g12a-usb2.c235
-rw-r--r--drivers/reset/reset-meson.c42
-rw-r--r--drivers/rng/meson-rng.c72
-rw-r--r--drivers/sm/Kconfig9
-rw-r--r--drivers/sm/Makefile5
-rw-r--r--drivers/sm/meson-sm.c198
-rw-r--r--drivers/sm/sandbox-sm.c76
-rw-r--r--drivers/sm/sm-uclass.c55
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c79
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, &regs);
+
+ if (!priv->sm_shmem_in)
+ return -ENOMEM;
+
+ priv->sm_shmem_out =
+ (void *)__meson_sm_call(priv->data->cmd_get_shmem_out, &regs);
+
+ 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,
+ },
{ }
};