From 9af0c7732bf1df29138bb63712dc3fcbc6d821af Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:25:51 +0000 Subject: pci: pcie_dw_rockchip: Configure number of lanes and link width speed Set number of lanes and link width speed control register based on the num-lanes property. Code imported almost 1:1 from dw_pcie_setup in mainline linux. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/pci/pcie_dw_rockchip.c | 58 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie_dw_rockchip.c b/drivers/pci/pcie_dw_rockchip.c index 1a35fae5c3a..bc4635f6713 100644 --- a/drivers/pci/pcie_dw_rockchip.c +++ b/drivers/pci/pcie_dw_rockchip.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ struct rk_pcie { struct reset_ctl_bulk rsts; struct gpio_desc rst_gpio; u32 gen; + u32 num_lanes; }; /* Parameters for the waiting for iATU enabled routine */ @@ -152,12 +154,13 @@ static inline void rk_pcie_writel_apb(struct rk_pcie *rk_pcie, u32 reg, * rk_pcie_configure() - Configure link capabilities and speed * * @rk_pcie: Pointer to the PCI controller state - * @cap_speed: The capabilities and speed to configure * * Configure the link capabilities and speed in the PCIe root complex. */ -static void rk_pcie_configure(struct rk_pcie *pci, u32 cap_speed) +static void rk_pcie_configure(struct rk_pcie *pci) { + u32 val; + dw_pcie_dbi_write_enable(&pci->dw, true); /* Disable BAR 0 and BAR 1 */ @@ -167,11 +170,49 @@ static void rk_pcie_configure(struct rk_pcie *pci, u32 cap_speed) PCI_BASE_ADDRESS_1); clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CAPABILITY, - TARGET_LINK_SPEED_MASK, cap_speed); + TARGET_LINK_SPEED_MASK, pci->gen); clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CTL_2, - TARGET_LINK_SPEED_MASK, cap_speed); + TARGET_LINK_SPEED_MASK, pci->gen); + + /* Set the number of lanes */ + val = readl(pci->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_FAST_LINK_MODE; + val |= PORT_LINK_DLL_LINK_EN; + val &= ~PORT_LINK_MODE_MASK; + switch (pci->num_lanes) { + case 1: + val |= PORT_LINK_MODE_1_LANES; + break; + case 2: + val |= PORT_LINK_MODE_2_LANES; + break; + case 4: + val |= PORT_LINK_MODE_4_LANES; + break; + default: + dev_err(pci->dw.dev, "num-lanes %u: invalid value\n", pci->num_lanes); + goto out; + } + writel(val, pci->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + + /* Set link width speed control register */ + val = readl(pci->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (pci->num_lanes) { + case 1: + val |= PORT_LOGIC_LINK_WIDTH_1_LANES; + break; + case 2: + val |= PORT_LOGIC_LINK_WIDTH_2_LANES; + break; + case 4: + val |= PORT_LOGIC_LINK_WIDTH_4_LANES; + break; + } + writel(val, pci->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); +out: dw_pcie_dbi_write_enable(&pci->dw, false); } @@ -231,11 +272,10 @@ static int is_link_up(struct rk_pcie *priv) * rk_pcie_link_up() - Wait for the link to come up * * @rk_pcie: Pointer to the PCI controller state - * @cap_speed: Desired link speed * * Return: 1 (true) for active line and negetive (false) for no link (timeout) */ -static int rk_pcie_link_up(struct rk_pcie *priv, u32 cap_speed) +static int rk_pcie_link_up(struct rk_pcie *priv) { int retries; @@ -245,7 +285,7 @@ static int rk_pcie_link_up(struct rk_pcie *priv, u32 cap_speed) } /* DW pre link configurations */ - rk_pcie_configure(priv, cap_speed); + rk_pcie_configure(priv); rk_pcie_disable_ltssm(priv); rk_pcie_link_status_clear(priv); @@ -341,7 +381,7 @@ static int rockchip_pcie_init_port(struct udevice *dev) rk_pcie_writel_apb(priv, 0x0, 0xf00040); pcie_dw_setup_host(&priv->dw); - ret = rk_pcie_link_up(priv, priv->gen); + ret = rk_pcie_link_up(priv); if (ret < 0) goto err_link_up; @@ -419,6 +459,8 @@ static int rockchip_pcie_parse_dt(struct udevice *dev) priv->gen = dev_read_u32_default(dev, "max-link-speed", LINK_SPEED_GEN_3); + priv->num_lanes = dev_read_u32_default(dev, "num-lanes", 1); + return 0; rockchip_pcie_parse_dt_err_phy_get_by_index: -- cgit v1.3.1 From 3b39592e8e245fc5c7b0a003ac65672ce9cfaf0f Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:04:29 +0000 Subject: phy: rockchip: snps-pcie3: Refactor to use clk_bulk API Change to use clk_bulk API and syscon_regmap_lookup_by_phandle to simplify in preparation for upcoming support of a RK3588 variant. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 58 ++++---------------------- 1 file changed, 9 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 66c75f98e6d..3053543a332 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -26,17 +26,13 @@ * @mmio: The base address of PHY internal registers * @phy_grf: The regmap for controlling pipe signal * @p30phy: The reset signal for PHY - * @ref_clk_m: The reference clock of M for PHY - * @ref_clk_n: The reference clock of N for PHY - * @pclk: The clock for accessing PHY blocks + * @clks: The clocks for PHY */ struct rockchip_p3phy_priv { void __iomem *mmio; struct regmap *phy_grf; struct reset_ctl p30phy; - struct clk ref_clk_m; - struct clk ref_clk_n; - struct clk pclk; + struct clk_bulk clks; }; static int rochchip_p3phy_init(struct phy *phy) @@ -44,18 +40,10 @@ static int rochchip_p3phy_init(struct phy *phy) struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); int ret; - ret = clk_enable(&priv->ref_clk_m); - if (ret < 0 && ret != -ENOSYS) + ret = clk_enable_bulk(&priv->clks); + if (ret) return ret; - ret = clk_enable(&priv->ref_clk_n); - if (ret < 0 && ret != -ENOSYS) - goto err_ref; - - ret = clk_enable(&priv->pclk); - if (ret < 0 && ret != -ENOSYS) - goto err_pclk; - reset_assert(&priv->p30phy); udelay(1); @@ -67,21 +55,13 @@ static int rochchip_p3phy_init(struct phy *phy) udelay(1); return 0; -err_pclk: - clk_disable(&priv->ref_clk_n); -err_ref: - clk_disable(&priv->ref_clk_m); - - return ret; } static int rochchip_p3phy_exit(struct phy *phy) { struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); - clk_disable(&priv->ref_clk_m); - clk_disable(&priv->ref_clk_n); - clk_disable(&priv->pclk); + clk_disable_bulk(&priv->clks); reset_assert(&priv->p30phy); return 0; @@ -90,21 +70,13 @@ static int rochchip_p3phy_exit(struct phy *phy) static int rockchip_p3phy_probe(struct udevice *dev) { struct rockchip_p3phy_priv *priv = dev_get_priv(dev); - struct udevice *syscon; int ret; priv->mmio = dev_read_addr_ptr(dev); if (!priv->mmio) return -EINVAL; - ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, - "rockchip,phy-grf", &syscon); - if (ret) { - pr_err("unable to find syscon device for rockchip,phy-grf\n"); - return ret; - } - - priv->phy_grf = syscon_get_regmap(syscon); + priv->phy_grf = syscon_regmap_lookup_by_phandle(dev, "rockchip,phy-grf"); if (IS_ERR(priv->phy_grf)) { dev_err(dev, "failed to find rockchip,phy_grf regmap\n"); return PTR_ERR(priv->phy_grf); @@ -116,22 +88,10 @@ static int rockchip_p3phy_probe(struct udevice *dev) return ret; } - ret = clk_get_by_name(dev, "refclk_m", &priv->ref_clk_m); + ret = clk_get_bulk(dev, &priv->clks); if (ret) { - dev_err(dev, "failed to find ref clock M\n"); - return PTR_ERR(&priv->ref_clk_m); - } - - ret = clk_get_by_name(dev, "refclk_n", &priv->ref_clk_n); - if (ret) { - dev_err(dev, "failed to find ref clock N\n"); - return PTR_ERR(&priv->ref_clk_n); - } - - ret = clk_get_by_name(dev, "pclk", &priv->pclk); - if (ret) { - dev_err(dev, "failed to find pclk\n"); - return PTR_ERR(&priv->pclk); + dev_err(dev, "failed to get clocks\n"); + return ret; } return 0; -- cgit v1.3.1 From 6cacdf842db5e62e9c26d015eddadd2f2410a6de Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:04:30 +0000 Subject: phy: rockchip: snps-pcie3: Refactor to use a phy_init ops Add a phy_init ops in preparation for upcoming support of a RK3588 variant in the driver. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 40 ++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 3053543a332..b76b5386bef 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -35,8 +35,32 @@ struct rockchip_p3phy_priv { struct clk_bulk clks; }; +struct rockchip_p3phy_ops { + int (*phy_init)(struct phy *phy); +}; + +static int rockchip_p3phy_rk3568_init(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); + + /* Deassert PCIe PMA output clamp mode */ + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, + (0x1 << 15) | (0x1 << 31)); + + reset_deassert(&priv->p30phy); + udelay(1); + + return 0; +} + +static const struct rockchip_p3phy_ops rk3568_ops = { + .phy_init = rockchip_p3phy_rk3568_init, +}; + static int rochchip_p3phy_init(struct phy *phy) { + struct rockchip_p3phy_ops *ops = + (struct rockchip_p3phy_ops *)dev_get_driver_data(phy->dev); struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); int ret; @@ -47,14 +71,11 @@ static int rochchip_p3phy_init(struct phy *phy) reset_assert(&priv->p30phy); udelay(1); - /* Deassert PCIe PMA output clamp mode */ - regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, - (0x1 << 15) | (0x1 << 31)); - - reset_deassert(&priv->p30phy); - udelay(1); + ret = ops->phy_init(phy); + if (ret) + clk_disable_bulk(&priv->clks); - return 0; + return ret; } static int rochchip_p3phy_exit(struct phy *phy) @@ -103,7 +124,10 @@ static struct phy_ops rochchip_p3phy_ops = { }; static const struct udevice_id rockchip_p3phy_of_match[] = { - { .compatible = "rockchip,rk3568-pcie3-phy" }, + { + .compatible = "rockchip,rk3568-pcie3-phy", + .data = (ulong)&rk3568_ops, + }, { }, }; -- cgit v1.3.1 From 1ebebfcc25bc8963cbdc6e35504160e5b745cabd Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:28:33 +0000 Subject: phy: rockchip: snps-pcie3: Add bifurcation support for RK3568 Configure aggregation or bifurcation mode on RK3568 based on the value of data-lanes property. Code imported almost 1:1 from mainline linux driver. Fixes: 6ec62b6ca698 ("phy: rockchip: Add Rockchip Synopsys PCIe 3.0 PHY") Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 65 +++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index b76b5386bef..642819b1f67 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -16,9 +16,16 @@ #include #include -#define GRF_PCIE30PHY_CON1 0x4 -#define GRF_PCIE30PHY_CON6 0x18 -#define GRF_PCIE30PHY_CON9 0x24 +/* Register for RK3568 */ +#define GRF_PCIE30PHY_CON1 0x4 +#define GRF_PCIE30PHY_CON6 0x18 +#define GRF_PCIE30PHY_CON9 0x24 +#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31)) +#define GRF_PCIE30PHY_STATUS0 0x80 +#define GRF_PCIE30PHY_WR_EN (0xf << 16) +#define SRAM_INIT_DONE(reg) (reg & BIT(14)) + +#define RK3568_BIFURCATION_LANE_0_1 BIT(0) /** * struct rockchip_p3phy_priv - RK DW PCIe PHY state @@ -27,12 +34,16 @@ * @phy_grf: The regmap for controlling pipe signal * @p30phy: The reset signal for PHY * @clks: The clocks for PHY + * @num_lanes: The number of lane to controller mappings + * @lanes: The lane to controller mapping */ struct rockchip_p3phy_priv { void __iomem *mmio; struct regmap *phy_grf; struct reset_ctl p30phy; struct clk_bulk clks; + int num_lanes; + u32 lanes[4]; }; struct rockchip_p3phy_ops { @@ -42,15 +53,40 @@ struct rockchip_p3phy_ops { static int rockchip_p3phy_rk3568_init(struct phy *phy) { struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); + bool bifurcation = false; + int ret; + u32 reg; /* Deassert PCIe PMA output clamp mode */ - regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, - (0x1 << 15) | (0x1 << 31)); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM); + + for (int i = 0; i < priv->num_lanes; i++) { + if (priv->lanes[i] > 1) + bifurcation = true; + } + + /* Set bifurcation if needed, and it doesn't care RC/EP */ + if (bifurcation) { + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, + GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1, + GRF_PCIE30PHY_DA_OCM); + } else { + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, + GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1); + } reset_deassert(&priv->p30phy); udelay(1); - return 0; + ret = regmap_read_poll_timeout(priv->phy_grf, + GRF_PCIE30PHY_STATUS0, + reg, SRAM_INIT_DONE(reg), + 0, 500); + if (ret) + dev_err(phy->dev, "lock failed 0x%x\n", reg); + + return ret; } static const struct rockchip_p3phy_ops rk3568_ops = { @@ -103,6 +139,23 @@ static int rockchip_p3phy_probe(struct udevice *dev) return PTR_ERR(priv->phy_grf); } + ret = dev_read_size(dev, "data-lanes"); + if (ret > 0) { + priv->num_lanes = ret / sizeof(u32); + if (priv->num_lanes < 2 || + priv->num_lanes > ARRAY_SIZE(priv->lanes)) { + dev_err(dev, "unsupported data-lanes property size\n"); + return -EINVAL; + } + + ret = dev_read_u32_array(dev, "data-lanes", priv->lanes, + priv->num_lanes); + if (ret) { + dev_err(dev, "failed to read data-lanes property\n"); + return ret; + } + } + ret = reset_get_by_name(dev, "phy", &priv->p30phy); if (ret) { dev_err(dev, "no phy reset control specified\n"); -- cgit v1.3.1 From 50e54e80679b4ab45c84687c77309aebc6f7b981 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:04:32 +0000 Subject: phy: rockchip: snps-pcie3: Add support for RK3588 Add support for the RK3588 variant to the driver. Code imported almost 1:1 from mainline linux driver. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 85 ++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 642819b1f67..a4392daf4c9 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -27,6 +27,17 @@ #define RK3568_BIFURCATION_LANE_0_1 BIT(0) +/* Register for RK3588 */ +#define PHP_GRF_PCIESEL_CON 0x100 +#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 +#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 +#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 +#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0)) + +#define RK3588_BIFURCATION_LANE_0_1 BIT(0) +#define RK3588_BIFURCATION_LANE_2_3 BIT(1) +#define RK3588_LANE_AGGREGATION BIT(2) + /** * struct rockchip_p3phy_priv - RK DW PCIe PHY state * @@ -40,6 +51,7 @@ struct rockchip_p3phy_priv { void __iomem *mmio; struct regmap *phy_grf; + struct regmap *pipe_grf; struct reset_ctl p30phy; struct clk_bulk clks; int num_lanes; @@ -93,6 +105,66 @@ static const struct rockchip_p3phy_ops rk3568_ops = { .phy_init = rockchip_p3phy_rk3568_init, }; +static int rockchip_p3phy_rk3588_init(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = dev_get_priv(phy->dev); + u32 reg = 0; + u8 mode = 0; + int ret; + + /* Deassert PCIe PMA output clamp mode */ + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, + BIT(8) | BIT(24)); + + /* Set bifurcation if needed */ + for (int i = 0; i < priv->num_lanes; i++) { + if (!priv->lanes[i]) + mode |= (BIT(i) << 3); + + if (priv->lanes[i] > 1) + mode |= (BIT(i) >> 1); + } + + if (!mode) { + reg = RK3588_LANE_AGGREGATION; + } else { + if (mode & (BIT(0) | BIT(1))) + reg |= RK3588_BIFURCATION_LANE_0_1; + + if (mode & (BIT(2) | BIT(3))) + reg |= RK3588_BIFURCATION_LANE_2_3; + } + + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, + (0x7 << 16) | reg); + + /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */ + reg = (mode & (BIT(6) | BIT(7))) >> 6; + if (reg) + regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON, + (reg << 16) | reg); + + reset_deassert(&priv->p30phy); + udelay(1); + + ret = regmap_read_poll_timeout(priv->phy_grf, + RK3588_PCIE3PHY_GRF_PHY0_STATUS1, + reg, RK3588_SRAM_INIT_DONE(reg), + 0, 500); + ret |= regmap_read_poll_timeout(priv->phy_grf, + RK3588_PCIE3PHY_GRF_PHY1_STATUS1, + reg, RK3588_SRAM_INIT_DONE(reg), + 0, 500); + if (ret) + dev_err(phy->dev, "lock failed 0x%x\n", reg); + + return ret; +} + +static const struct rockchip_p3phy_ops rk3588_ops = { + .phy_init = rockchip_p3phy_rk3588_init, +}; + static int rochchip_p3phy_init(struct phy *phy) { struct rockchip_p3phy_ops *ops = @@ -139,6 +211,15 @@ static int rockchip_p3phy_probe(struct udevice *dev) return PTR_ERR(priv->phy_grf); } + if (device_is_compatible(dev, "rockchip,rk3588-pcie3-phy")) { + priv->pipe_grf = + syscon_regmap_lookup_by_phandle(dev, "rockchip,pipe-grf"); + if (IS_ERR(priv->pipe_grf)) { + dev_err(dev, "failed to find rockchip,pipe_grf regmap\n"); + return PTR_ERR(priv->pipe_grf); + } + } + ret = dev_read_size(dev, "data-lanes"); if (ret > 0) { priv->num_lanes = ret / sizeof(u32); @@ -181,6 +262,10 @@ static const struct udevice_id rockchip_p3phy_of_match[] = { .compatible = "rockchip,rk3568-pcie3-phy", .data = (ulong)&rk3568_ops, }, + { + .compatible = "rockchip,rk3588-pcie3-phy", + .data = (ulong)&rk3588_ops, + }, { }, }; -- cgit v1.3.1 From b37260bca1aa562c6c99527d997c768a12da017b Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 2 Aug 2023 19:41:22 +0000 Subject: phy: rockchip: naneng-combphy: Use signal from comb PHY on RK3588 Route signal from comb PHY instead of PCIe3 PHY to PCIe1l0 and PCIe1l1. Fixes use of pcie2x1l0 on ROCK 5B. Code imported from mainline linux driver. Fixes: c5b4a012bca8 ("phy: rockchip: naneng-combphy: Support rk3588") Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-naneng-combphy.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index d5408ccac97..9ca66bf8db9 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -61,6 +61,8 @@ struct rockchip_combphy_grfcfg { struct combphy_reg pipe_con1_for_sata; struct combphy_reg pipe_sgmii_mac_sel; struct combphy_reg pipe_xpcs_phy_ready; + struct combphy_reg pipe_pcie1l0_sel; + struct combphy_reg pipe_pcie1l1_sel; struct combphy_reg u3otg0_port_en; struct combphy_reg u3otg1_port_en; }; @@ -435,6 +437,8 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) param_write(priv->phy_grf, &cfg->con1_for_pcie, true); param_write(priv->phy_grf, &cfg->con2_for_pcie, true); param_write(priv->phy_grf, &cfg->con3_for_pcie, true); + param_write(priv->pipe_grf, &cfg->pipe_pcie1l0_sel, true); + param_write(priv->pipe_grf, &cfg->pipe_pcie1l1_sel, true); break; case PHY_TYPE_USB3: param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); @@ -507,6 +511,8 @@ static const struct rockchip_combphy_grfcfg rk3588_combphy_grfcfgs = { /* pipe-grf */ .pipe_con0_for_sata = { 0x0000, 11, 5, 0x00, 0x22 }, .pipe_con1_for_sata = { 0x0000, 2, 0, 0x00, 0x2 }, + .pipe_pcie1l0_sel = { 0x0100, 0, 0, 0x01, 0x0 }, + .pipe_pcie1l1_sel = { 0x0100, 1, 1, 0x01, 0x0 }, }; static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = { -- cgit v1.3.1 From d99fb64a98af3bebf6b0c134291c4fb89e177aa2 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 21 Aug 2023 22:30:24 +0000 Subject: power: regulator: Only run autoset once for each regulator With the commit 4fcba5d556b4 ("regulator: implement basic reference counter"), keeping regulator enablement in balance become more important. Calling regulator_autoset multiple times on a fixed regulator increase the enable count for each call, resulting in an unbalanced enable count. Introduce a AUTOSET_DONE flag and use it to mark that autoset has run for the regulator. Return -EALREADY on any subsequent call to autoset. This fixes so that the enable count is only ever increased by one per regulator for autoset. Fixes: 4fcba5d556b4 ("regulator: implement basic reference counter") Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/power/regulator/regulator-uclass.c | 18 ++++++++++++++---- include/power/regulator.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 3a6ba69f6d5..77d101f262e 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -293,6 +293,9 @@ int regulator_autoset(struct udevice *dev) uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_DONE) + return -EALREADY; + ret = regulator_set_suspend_enable(dev, uc_pdata->suspend_on); if (ret == -ENOSYS) ret = 0; @@ -306,11 +309,15 @@ int regulator_autoset(struct udevice *dev) return ret; } - if (!uc_pdata->always_on && !uc_pdata->boot_on) - return -EMEDIUMTYPE; + if (!uc_pdata->always_on && !uc_pdata->boot_on) { + ret = -EMEDIUMTYPE; + goto out; + } - if (uc_pdata->type == REGULATOR_TYPE_FIXED) - return regulator_set_enable(dev, true); + if (uc_pdata->type == REGULATOR_TYPE_FIXED) { + ret = regulator_set_enable(dev, true); + goto out; + } if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) ret = regulator_set_value(dev, uc_pdata->min_uV); @@ -322,6 +329,9 @@ int regulator_autoset(struct udevice *dev) if (!ret) ret = regulator_set_enable(dev, true); +out: + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_DONE; + return ret; } diff --git a/include/power/regulator.h b/include/power/regulator.h index ff1bfc2435a..200652cb3d7 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -134,6 +134,7 @@ struct dm_regulator_mode { enum regulator_flag { REGULATOR_FLAG_AUTOSET_UV = 1 << 0, REGULATOR_FLAG_AUTOSET_UA = 1 << 1, + REGULATOR_FLAG_AUTOSET_DONE = 1 << 2, }; /** -- cgit v1.3.1 From 04c38c6c4936f353de36be60655f402922292a37 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Mon, 21 Aug 2023 22:30:25 +0000 Subject: regulator: rk8xx: Return correct voltage for buck converters Information from the first range group is always used to calculate the voltage returned for buck converters. This may result in wrong voltage reported back to the regulator_get_value caller. Traverse all the possible BUCK ranges to fix this issue. Fixes: addd062beacc ("power: pmic: rk816: support rk816 pmic") Fixes: b62280745e55 ("power: pmic: rk805: support rk805 pmic") Fixes: b4a35574b38d ("power: pmic: rk817: support rk817 pmic") Fixes: ee30068fa574 ("power: pmic: rk809: support rk809 pmic") Signed-off-by: Joseph Chen [jonas@kwiboo.se: fix checkpatch error, simplify buck get_value, update commit message] Signed-off-by: Jonas Karlman Reviewed-by: Simon Glass Reviewed-by: Kever Yang --- drivers/power/regulator/rk8xx.c | 76 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index e95640a39b0..9444daa85c1 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -88,62 +88,63 @@ struct rk8xx_reg_info { u8 config_reg; u8 vsel_mask; u8 min_sel; + u8 max_sel; }; static const struct rk8xx_reg_info rk808_buck[] = { - { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK808_BUCK_VSEL_MASK, }, - { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK808_BUCK_VSEL_MASK, }, - { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK808_BUCK_VSEL_MASK, }, - { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK808_BUCK4_VSEL_MASK, }, + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK808_BUCK_VSEL_MASK, 0x00, 0x3f }, + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK808_BUCK_VSEL_MASK, 0x00, 0x3f }, + { NA, NA, NA, NA, REG_BUCK3_CONFIG, NA, NA, NA }, + { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK808_BUCK4_VSEL_MASK, 0x00, 0x0f }, }; static const struct rk8xx_reg_info rk816_buck[] = { /* buck 1 */ - { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, }, - { 1800000, 200000, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, }, - { 2300000, 0, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, }, + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, 0x3b }, + { 1800000, 200000, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, 0x3e }, + { 2300000, 0, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, 0x3f }, /* buck 2 */ - { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, }, - { 1800000, 200000, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, }, - { 2300000, 0, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, }, + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, 0x3b }, + { 1800000, 200000, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, 0x3e }, + { 2300000, 0, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, 0x3f }, /* buck 3 */ - { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK818_BUCK_VSEL_MASK, }, + { NA, NA, NA, NA, REG_BUCK3_CONFIG, NA, NA, NA }, /* buck 4 */ - { 800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, }, + { 800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, 0x00, 0x1f }, }; static const struct rk8xx_reg_info rk809_buck5[] = { /* buck 5 */ - { 1500000, 0, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x00, }, - { 1800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x01, }, - { 2800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x04, }, - { 3300000, 300000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x06, }, + { 1500000, 0, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x00, 0x00 }, + { 1800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x01, 0x03 }, + { 2800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x04, 0x05 }, + { 3300000, 300000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x06, 0x07 }, }; static const struct rk8xx_reg_info rk817_buck[] = { /* buck 1 */ - { 500000, 12500, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x00, }, - { 1500000, 100000, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x50, }, - { 2400000, 0, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x59, }, + { 500000, 12500, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x00, 0x4f }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x50, 0x58 }, + { 2400000, 0, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x59, 0x7f }, /* buck 2 */ - { 500000, 12500, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x00, }, - { 1500000, 100000, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x50, }, - { 2400000, 0, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x59, }, + { 500000, 12500, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x00, 0x4f }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x50, 0x58 }, + { 2400000, 0, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x59, 0x7f }, /* buck 3 */ - { 500000, 12500, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x00, }, - { 1500000, 100000, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x50, }, - { 2400000, 0, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x59, }, + { 500000, 12500, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x00, 0x4f }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x50, 0x58 }, + { 2400000, 0, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x59, 0x7f }, /* buck 4 */ - { 500000, 12500, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x00, }, - { 1500000, 100000, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x50, }, - { 3400000, 0, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x63, }, + { 500000, 12500, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x00, 0x4f }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x50, 0x62 }, + { 3400000, 0, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x63, 0x7f }, }; static const struct rk8xx_reg_info rk818_buck[] = { - { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, }, - { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, }, - { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK818_BUCK_VSEL_MASK, }, - { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, }, + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, 0x3f }, + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, 0x3f }, + { NA, NA, NA, NA, REG_BUCK3_CONFIG, NA, NA, NA }, + { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, 0x00, 0x1f }, }; #ifdef ENABLE_DRIVER @@ -706,7 +707,6 @@ static int _ldo_get_suspend_enable(struct udevice *pmic, int ldo) static int buck_get_value(struct udevice *dev) { int buck = dev->driver_data - 1; - /* We assume level-1 voltage is enough for usage in U-Boot */ const struct rk8xx_reg_info *info = get_buck_reg(dev->parent, buck, 0); int mask = info->vsel_mask; int ret, val; @@ -717,9 +717,12 @@ static int buck_get_value(struct udevice *dev) ret = pmic_reg_read(dev->parent, info->vsel_reg); if (ret < 0) return ret; + val = ret & mask; + while (val > info->max_sel) + info++; - return info->min_uv + val * info->step_uv; + return info->min_uv + (val - info->min_sel) * info->step_uv; } static int buck_set_value(struct udevice *dev, int uvolt) @@ -732,7 +735,6 @@ static int buck_set_value(struct udevice *dev, int uvolt) static int buck_get_suspend_value(struct udevice *dev) { int buck = dev->driver_data - 1; - /* We assume level-1 voltage is enough for usage in U-Boot */ const struct rk8xx_reg_info *info = get_buck_reg(dev->parent, buck, 0); int mask = info->vsel_mask; int ret, val; @@ -745,8 +747,10 @@ static int buck_get_suspend_value(struct udevice *dev) return ret; val = ret & mask; + while (val > info->max_sel) + info++; - return info->min_uv + val * info->step_uv; + return info->min_uv + (val - info->min_sel) * info->step_uv; } static int buck_set_suspend_value(struct udevice *dev, int uvolt) -- cgit v1.3.1 From bb657ffdd688dc08073734a402914ec0a8492d53 Mon Sep 17 00:00:00 2001 From: shengfei Xu Date: Mon, 21 Aug 2023 22:30:26 +0000 Subject: regulator: rk8xx: Return correct voltage for switchout converters The voltage value for switchout converters is always reported as 0 uV. When the switch is enabled, it's voltage is same as input supply. Fix this by implementing get_value for switchout converters. Fixes: ee30068fa574 ("power: pmic: rk809: support rk809 pmic") Signed-off-by: shengfei Xu [jonas@kwiboo.se: fix checkpatch error, update commit message] Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/power/regulator/rk8xx.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index 9444daa85c1..e80bd6c3723 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -1032,6 +1032,25 @@ static int switch_get_suspend_enable(struct udevice *dev) */ static int switch_get_value(struct udevice *dev) { + static const char * const supply_name_rk809[] = { + "vcc9-supply", + "vcc8-supply", + }; + struct rk8xx_priv *priv = dev_get_priv(dev->parent); + struct udevice *supply; + int id = dev->driver_data - 1; + + if (!switch_get_enable(dev)) + return 0; + + if (priv->variant == RK809_ID) { + if (!uclass_get_device_by_phandle(UCLASS_REGULATOR, + dev->parent, + supply_name_rk809[id], + &supply)) + return regulator_get_value(supply); + } + return 0; } -- cgit v1.3.1 From 09329df25f69f76751691c5a6d7a9e6f6567bc4c Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 21 Aug 2023 22:30:28 +0000 Subject: rockchip: Port IO-domain driver for RK3568 from linux Port the Rockchip IO-domain driver for RK3568 from linux. The driver auto probe after bind to configure IO-domain based on the regulator voltage. Compared to the linux driver this driver is not notified about regulator voltage changes and only configure IO-domain based on the initial voltage autoset by the regulator. It is not recommended to enable MMC_IO_VOLTAGE or the mmc signal voltage and IO-domain may end up out of sync. Based on the linux commit 28b05a64e47c ("soc: rockchip: io-domain: add rk3568 support"). Signed-off-by: Jonas Karlman Reviewed-by: Simon Glass Reviewed-by: Kever Yang --- drivers/misc/Kconfig | 9 ++ drivers/misc/Makefile | 1 + drivers/misc/rockchip-io-domain.c | 167 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 drivers/misc/rockchip-io-domain.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c930e4a361b..fccd9b89b81 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -101,6 +101,15 @@ config ROCKCHIP_OTP addressing and a length or through child-nodes that are generated based on the e-fuse map retrieved from the DTS. +config ROCKCHIP_IODOMAIN + bool "Rockchip IO-domain driver support" + depends on DM_REGULATOR && ARCH_ROCKCHIP + default y if ROCKCHIP_RK3568 + help + Enable support for IO-domains in Rockchip SoCs. It is necessary + for the IO-domain setting of the SoC to match the voltage supplied + by the regulators. + config SIFIVE_OTP bool "SiFive eMemory OTP driver" depends on MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fd8805f34bd..b67b82358a6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o +obj-$(CONFIG_$(SPL_TPL_)ROCKCHIP_IODOMAIN) += rockchip-io-domain.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o obj-$(CONFIG_SIFIVE_OTP) += sifive-otp.o obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o diff --git a/drivers/misc/rockchip-io-domain.c b/drivers/misc/rockchip-io-domain.c new file mode 100644 index 00000000000..3f6227f993f --- /dev/null +++ b/drivers/misc/rockchip-io-domain.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip IO Voltage Domain driver + * + * Ported from linux drivers/soc/rockchip/io-domain.c + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_SUPPLIES 16 + +/* + * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under + * "Recommended Operating Conditions" for "Digital GPIO". When the typical + * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. + * + * They are used like this: + * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the + * SoC we're at 3.3. + * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider + * that to be an error. + */ +#define MAX_VOLTAGE_1_8 1980000 +#define MAX_VOLTAGE_3_3 3600000 + +#define RK3568_PMU_GRF_IO_VSEL0 0x0140 +#define RK3568_PMU_GRF_IO_VSEL1 0x0144 +#define RK3568_PMU_GRF_IO_VSEL2 0x0148 + +struct rockchip_iodomain_soc_data { + int grf_offset; + const char *supply_names[MAX_SUPPLIES]; + int (*write)(struct regmap *grf, int idx, int uV); +}; + +static int rk3568_iodomain_write(struct regmap *grf, int idx, int uV) +{ + u32 is_3v3 = uV > MAX_VOLTAGE_1_8; + u32 val0, val1; + int b; + + switch (idx) { + case 0: /* pmuio1 */ + break; + case 1: /* pmuio2 */ + b = idx; + val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b)); + b = idx + 4; + val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0); + + regmap_write(grf, RK3568_PMU_GRF_IO_VSEL2, val0); + regmap_write(grf, RK3568_PMU_GRF_IO_VSEL2, val1); + break; + case 3: /* vccio2 */ + break; + case 2: /* vccio1 */ + case 4: /* vccio3 */ + case 5: /* vccio4 */ + case 6: /* vccio5 */ + case 7: /* vccio6 */ + case 8: /* vccio7 */ + b = idx - 1; + val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b)); + val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0); + + regmap_write(grf, RK3568_PMU_GRF_IO_VSEL0, val0); + regmap_write(grf, RK3568_PMU_GRF_IO_VSEL1, val1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = { + .grf_offset = 0x140, + .supply_names = { + NULL, + "pmuio2-supply", + "vccio1-supply", + NULL, + "vccio3-supply", + "vccio4-supply", + "vccio5-supply", + "vccio6-supply", + "vccio7-supply", + }, + .write = rk3568_iodomain_write, +}; + +static const struct udevice_id rockchip_iodomain_ids[] = { + { + .compatible = "rockchip,rk3568-pmu-io-voltage-domain", + .data = (ulong)&soc_data_rk3568_pmu, + }, + { } +}; + +static int rockchip_iodomain_bind(struct udevice *dev) +{ + /* + * According to the Hardware Design Guide, IO-domain configuration must + * be consistent with the power supply voltage (1.8V or 3.3V). + * Probe after bind to configure IO-domain voltage early during boot. + */ + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); + + return 0; +} + +static int rockchip_iodomain_probe(struct udevice *dev) +{ + struct rockchip_iodomain_soc_data *soc_data = + (struct rockchip_iodomain_soc_data *)dev_get_driver_data(dev); + struct regmap *grf; + int ret; + + grf = syscon_get_regmap(dev_get_parent(dev)); + if (IS_ERR(grf)) + return PTR_ERR(grf); + + for (int i = 0; i < MAX_SUPPLIES; i++) { + const char *supply_name = soc_data->supply_names[i]; + struct udevice *reg; + int uV; + + if (!supply_name) + continue; + + ret = device_get_supply_regulator(dev, supply_name, ®); + if (ret) + continue; + + ret = regulator_autoset(reg); + if (ret && ret != -EALREADY && ret != -EMEDIUMTYPE && + ret != -ENOSYS) + continue; + + uV = regulator_get_value(reg); + if (uV <= 0) + continue; + + if (uV > MAX_VOLTAGE_3_3) { + dev_crit(dev, "%s: %d uV is too high. May damage SoC!\n", + supply_name, uV); + continue; + } + + soc_data->write(grf, i, uV); + } + + return 0; +} + +U_BOOT_DRIVER(rockchip_iodomain) = { + .name = "rockchip_iodomain", + .id = UCLASS_NOP, + .of_match = rockchip_iodomain_ids, + .bind = rockchip_iodomain_bind, + .probe = rockchip_iodomain_probe, +}; -- cgit v1.3.1 From 4ad1dfc7ab9080b9d826b16ce064b158bb5bc101 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 1 Oct 2023 19:17:16 +0000 Subject: net: dwc_eth_qos: Drop unused rx_pkt from eqos_priv rx_pkt is allocated and not used for anything, remove it. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang Reviewed-by: Ramon Fried --- drivers/net/dwc_eth_qos.c | 11 ----------- drivers/net/dwc_eth_qos.h | 1 - 2 files changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 9b1a9e69bf8..c7339747930 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1315,22 +1315,12 @@ static int eqos_probe_resources_core(struct udevice *dev) } debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf); - eqos->rx_pkt = malloc(EQOS_MAX_PACKET_SIZE); - if (!eqos->rx_pkt) { - debug("%s: malloc(rx_pkt) failed\n", __func__); - ret = -ENOMEM; - goto err_free_rx_dma_buf; - } - debug("%s: rx_pkt=%p\n", __func__, eqos->rx_pkt); - eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf, EQOS_MAX_PACKET_SIZE * EQOS_DESCRIPTORS_RX); debug("%s: OK\n", __func__); return 0; -err_free_rx_dma_buf: - free(eqos->rx_dma_buf); err_free_tx_dma_buf: free(eqos->tx_dma_buf); err_free_descs: @@ -1349,7 +1339,6 @@ static int eqos_remove_resources_core(struct udevice *dev) debug("%s(dev=%p):\n", __func__, dev); - free(eqos->rx_pkt); free(eqos->rx_dma_buf); free(eqos->tx_dma_buf); eqos_free_descs(eqos->rx_descs); diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h index a6b719af809..06a082da72e 100644 --- a/drivers/net/dwc_eth_qos.h +++ b/drivers/net/dwc_eth_qos.h @@ -273,7 +273,6 @@ struct eqos_priv { unsigned int desc_per_cacheline; void *tx_dma_buf; void *rx_dma_buf; - void *rx_pkt; bool started; bool reg_access_ok; bool clk_ck_enabled; -- cgit v1.3.1 From ded6014dfdcf8a9a73fc67eee72d40cdfff4e1fc Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 1 Oct 2023 19:17:17 +0000 Subject: net: dwc_eth_qos: Return error code when start fails Return error code when phy_connect fails or no link can be established. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang Reviewed-by: Ramon Fried --- drivers/net/dwc_eth_qos.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index c7339747930..27be5cf70b4 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -812,6 +812,7 @@ static int eqos_start(struct udevice *dev) if (!eqos->phy) { pr_err("phy_connect() failed"); + ret = -ENODEV; goto err_stop_resets; } @@ -839,6 +840,7 @@ static int eqos_start(struct udevice *dev) if (!eqos->phy->link) { pr_err("No link"); + ret = -EAGAIN; goto err_shutdown_phy; } -- cgit v1.3.1 From 95446f4a4dc73e956687d9d75b049bcab5fd7e49 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 1 Oct 2023 19:17:18 +0000 Subject: net: dwc_eth_qos: Stop spam of RX packet not available message Remove spam of RX packet not available debug messages when waiting to receive a packet. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang Reviewed-by: Ramon Fried --- drivers/net/dwc_eth_qos.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 27be5cf70b4..8e29f7b6c8f 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1194,14 +1194,12 @@ static int eqos_recv(struct udevice *dev, int flags, uchar **packetp) struct eqos_desc *rx_desc; int length; - debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags); - rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true); eqos->config->ops->eqos_inval_desc(rx_desc); - if (rx_desc->des3 & EQOS_DESC3_OWN) { - debug("%s: RX packet not available\n", __func__); + if (rx_desc->des3 & EQOS_DESC3_OWN) return -EAGAIN; - } + + debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags); *packetp = eqos->rx_dma_buf + (eqos->rx_desc_idx * EQOS_MAX_PACKET_SIZE); -- cgit v1.3.1 From 8e76ff61a3c454af6e1ea81012b1c9f80b326bc8 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 1 Oct 2023 19:17:19 +0000 Subject: net: dwc_eth_qos: Add glue driver for GMAC on Rockchip RK3568 Add a new glue driver for Rockchip SoCs, i.e RK3568, with a GMAC based on Synopsys DWC Ethernet QoS IP. rk_gmac_ops was ported from linux commit: 3bb3d6b1c195 ("net: stmmac: Add RK3566/RK3568 SoC support") Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang Reviewed-by: Ramon Fried --- drivers/net/Kconfig | 8 + drivers/net/Makefile | 1 + drivers/net/dwc_eth_qos.c | 8 +- drivers/net/dwc_eth_qos.h | 2 + drivers/net/dwc_eth_qos_rockchip.c | 357 +++++++++++++++++++++++++++++++++++++ 5 files changed, 374 insertions(+), 2 deletions(-) create mode 100644 drivers/net/dwc_eth_qos_rockchip.c (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0ed39a61e4d..29304fd7775 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -225,6 +225,14 @@ config DWC_ETH_QOS_IMX The Synopsys Designware Ethernet QOS IP block with the specific configuration used in IMX soc. +config DWC_ETH_QOS_ROCKCHIP + bool "Synopsys DWC Ethernet QOS device support for Rockchip SoCs" + depends on DWC_ETH_QOS + select DM_ETH_PHY + help + The Synopsys Designware Ethernet QOS IP block with specific + configuration used in Rockchip SoCs. + config DWC_ETH_QOS_STM32 bool "Synopsys DWC Ethernet QOS device support for STM32" depends on DWC_ETH_QOS diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d4af253b6f2..1d444f5b4a6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o +obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o obj-$(CONFIG_DWC_ETH_QOS_STARFIVE) += dwc_eth_qos_starfive.o obj-$(CONFIG_E1000) += e1000.o diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 8e29f7b6c8f..690e8a34af6 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1708,7 +1708,12 @@ static const struct udevice_id eqos_ids[] = { .data = (ulong)&eqos_imx_config }, #endif - +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ROCKCHIP) + { + .compatible = "rockchip,rk3568-gmac", + .data = (ulong)&eqos_rockchip_config + }, +#endif #if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM) { .compatible = "qcom,qcs404-ethqos", @@ -1721,7 +1726,6 @@ static const struct udevice_id eqos_ids[] = { .data = (ulong)&eqos_jh7110_config }, #endif - { } }; diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h index 06a082da72e..e3222e1e17e 100644 --- a/drivers/net/dwc_eth_qos.h +++ b/drivers/net/dwc_eth_qos.h @@ -82,6 +82,7 @@ struct eqos_mac_regs { #define EQOS_MAC_MDIO_ADDRESS_PA_SHIFT 21 #define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT 16 #define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT 8 +#define EQOS_MAC_MDIO_ADDRESS_CR_100_150 1 #define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2 #define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5 #define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4) @@ -287,5 +288,6 @@ void eqos_flush_buffer_generic(void *buf, size_t size); int eqos_null_ops(struct udevice *dev); extern struct eqos_config eqos_imx_config; +extern struct eqos_config eqos_rockchip_config; extern struct eqos_config eqos_qcom_config; extern struct eqos_config eqos_jh7110_config; diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c new file mode 100644 index 00000000000..9d57e8db764 --- /dev/null +++ b/drivers/net/dwc_eth_qos_rockchip.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright Contributors to the U-Boot project. + * + * rk_gmac_ops ported from linux drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c + * + * Ported code is intentionally left as close as possible with linux counter + * part in order to simplify future porting of fixes and support for other SoCs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dwc_eth_qos.h" + +struct rk_gmac_ops { + const char *compatible; + int (*set_to_rgmii)(struct udevice *dev, + int tx_delay, int rx_delay); + int (*set_to_rmii)(struct udevice *dev); + int (*set_gmac_speed)(struct udevice *dev); + u32 regs[3]; +}; + +struct rockchip_platform_data { + struct reset_ctl_bulk resets; + const struct rk_gmac_ops *ops; + int id; + struct regmap *grf; +}; + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_BIT(nr) (BIT(nr) | BIT((nr) + 16)) +#define GRF_CLR_BIT(nr) (BIT((nr) + 16)) + +#define RK3568_GRF_GMAC0_CON0 0x0380 +#define RK3568_GRF_GMAC0_CON1 0x0384 +#define RK3568_GRF_GMAC1_CON0 0x0388 +#define RK3568_GRF_GMAC1_CON1 0x038c + +/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */ +#define RK3568_GMAC_PHY_INTF_SEL_RGMII \ + (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6)) +#define RK3568_GMAC_PHY_INTF_SEL_RMII \ + (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6)) +#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3) +#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) +#define RK3568_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +#define RK3568_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0) +#define RK3568_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0) + +/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */ +#define RK3568_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RK3568_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static int rk3568_set_to_rgmii(struct udevice *dev, + int tx_delay, int rx_delay) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 con0, con1; + + con0 = (data->id == 1) ? RK3568_GRF_GMAC1_CON0 : + RK3568_GRF_GMAC0_CON0; + con1 = (data->id == 1) ? RK3568_GRF_GMAC1_CON1 : + RK3568_GRF_GMAC0_CON1; + + regmap_write(data->grf, con0, + RK3568_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3568_GMAC_CLK_TX_DL_CFG(tx_delay)); + + regmap_write(data->grf, con1, + RK3568_GMAC_PHY_INTF_SEL_RGMII | + RK3568_GMAC_RXCLK_DLY_ENABLE | + RK3568_GMAC_TXCLK_DLY_ENABLE); + + return 0; +} + +static int rk3568_set_to_rmii(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 con1; + + con1 = (data->id == 1) ? RK3568_GRF_GMAC1_CON1 : + RK3568_GRF_GMAC0_CON1; + regmap_write(data->grf, con1, RK3568_GMAC_PHY_INTF_SEL_RMII); + + return 0; +} + +static int rk3568_set_gmac_speed(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + ulong rate; + int ret; + + switch (eqos->phy->speed) { + case SPEED_10: + rate = 2500000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_1000: + rate = 125000000; + break; + default: + return -EINVAL; + } + + ret = clk_set_rate(&eqos->clk_tx, rate); + if (ret < 0) + return ret; + + return 0; +} + +static const struct rk_gmac_ops rk_gmac_ops[] = { + { + .compatible = "rockchip,rk3568-gmac", + .set_to_rgmii = rk3568_set_to_rgmii, + .set_to_rmii = rk3568_set_to_rmii, + .set_gmac_speed = rk3568_set_gmac_speed, + .regs = { + 0xfe2a0000, /* gmac0 */ + 0xfe010000, /* gmac1 */ + 0x0, /* sentinel */ + }, + }, + { } +}; + +static const struct rk_gmac_ops *get_rk_gmac_ops(struct udevice *dev) +{ + const struct rk_gmac_ops *ops = rk_gmac_ops; + + while (ops->compatible) { + if (device_is_compatible(dev, ops->compatible)) + return ops; + ops++; + } + + return NULL; +} + +static int eqos_probe_resources_rk(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data; + int reset_flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE; + int ret; + + data = calloc(1, sizeof(struct rockchip_platform_data)); + if (!data) + return -ENOMEM; + + data->ops = get_rk_gmac_ops(dev); + if (!data->ops) { + ret = -EINVAL; + goto err_free; + } + + for (int i = 0; data->ops->regs[i]; i++) { + if (data->ops->regs[i] == (u32)eqos->regs) { + data->id = i; + break; + } + } + + pdata->priv_pdata = data; + pdata->phy_interface = eqos->config->interface(dev); + pdata->max_speed = eqos->max_speed; + + if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) { + pr_err("Invalid PHY interface\n"); + ret = -EINVAL; + goto err_free; + } + + data->grf = syscon_regmap_lookup_by_phandle(dev, "rockchip,grf"); + if (IS_ERR(data->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + ret = -EINVAL; + goto err_free; + } + + ret = reset_get_bulk(dev, &data->resets); + if (ret < 0) + goto err_free; + + reset_assert_bulk(&data->resets); + + ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus); + if (ret) { + dev_dbg(dev, "clk_get_by_name(stmmaceth) failed: %d", ret); + goto err_release_resets; + } + + ret = clk_get_by_name(dev, "clk_mac_speed", &eqos->clk_tx); + if (ret) { + dev_dbg(dev, "clk_get_by_name(clk_mac_speed) failed: %d", ret); + goto err_free_clk_master_bus; + } + + /* snps,reset props are deprecated, do bare minimum to support them */ + if (dev_read_bool(dev, "snps,reset-active-low")) + reset_flags |= GPIOD_ACTIVE_LOW; + + dev_read_u32_array(dev, "snps,reset-delays-us", eqos->reset_delays, 3); + + gpio_request_by_name(dev, "snps,reset-gpio", 0, + &eqos->phy_reset_gpio, reset_flags); + + return 0; + +err_free_clk_master_bus: + clk_free(&eqos->clk_master_bus); +err_release_resets: + reset_release_bulk(&data->resets); +err_free: + free(data); + + return ret; +} + +static int eqos_remove_resources_rk(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) + dm_gpio_free(dev, &eqos->phy_reset_gpio); + + clk_free(&eqos->clk_tx); + clk_free(&eqos->clk_master_bus); + reset_release_bulk(&data->resets); + free(data); + + return 0; +} + +static int eqos_stop_resets_rk(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + return reset_assert_bulk(&data->resets); +} + +static int eqos_start_resets_rk(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + return reset_deassert_bulk(&data->resets); +} + +static int eqos_stop_clks_rk(struct udevice *dev) +{ + return 0; +} + +static int eqos_start_clks_rk(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + int tx_delay, rx_delay, ret; + + if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) { + udelay(eqos->reset_delays[1]); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); + if (ret < 0) + return ret; + + udelay(eqos->reset_delays[2]); + } + + tx_delay = dev_read_u32_default(dev, "tx_delay", 0x30); + rx_delay = dev_read_u32_default(dev, "rx_delay", 0x10); + + switch (pdata->phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + return data->ops->set_to_rgmii(dev, tx_delay, rx_delay); + case PHY_INTERFACE_MODE_RGMII_ID: + return data->ops->set_to_rgmii(dev, 0, 0); + case PHY_INTERFACE_MODE_RGMII_RXID: + return data->ops->set_to_rgmii(dev, tx_delay, 0); + case PHY_INTERFACE_MODE_RGMII_TXID: + return data->ops->set_to_rgmii(dev, 0, rx_delay); + case PHY_INTERFACE_MODE_RMII: + return data->ops->set_to_rmii(dev); + } + + return -EINVAL; +} + +static int eqos_set_tx_clk_speed_rk(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + return data->ops->set_gmac_speed(dev); +} + +static ulong eqos_get_tick_clk_rate_rk(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + return clk_get_rate(&eqos->clk_master_bus); +} + +static struct eqos_ops eqos_rockchip_ops = { + .eqos_inval_desc = eqos_inval_desc_generic, + .eqos_flush_desc = eqos_flush_desc_generic, + .eqos_inval_buffer = eqos_inval_buffer_generic, + .eqos_flush_buffer = eqos_flush_buffer_generic, + .eqos_probe_resources = eqos_probe_resources_rk, + .eqos_remove_resources = eqos_remove_resources_rk, + .eqos_stop_resets = eqos_stop_resets_rk, + .eqos_start_resets = eqos_start_resets_rk, + .eqos_stop_clks = eqos_stop_clks_rk, + .eqos_start_clks = eqos_start_clks_rk, + .eqos_calibrate_pads = eqos_null_ops, + .eqos_disable_calibration = eqos_null_ops, + .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_rk, + .eqos_get_enetaddr = eqos_null_ops, + .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_rk, +}; + +struct eqos_config eqos_rockchip_config = { + .reg_access_always_ok = false, + .mdio_wait = 10, + .swr_wait = 50, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, + .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_100_150, + .axi_bus_width = EQOS_AXI_WIDTH_64, + .interface = dev_read_phy_mode, + .ops = &eqos_rockchip_ops, +}; -- cgit v1.3.1 From 01b8ed4754ff200cdb820fec77cb3bcb82e0e499 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 1 Oct 2023 19:17:20 +0000 Subject: net: dwc_eth_qos_rockchip: Add support for RK3588 Add rk_gmac_ops and other special handling that is needed for GMAC to work on RK3588. rk_gmac_ops was ported from linux commits: 2f2b60a0ec28 ("net: ethernet: stmmac: dwmac-rk: Add gmac support for rk3588") 88619e77b33d ("net: stmmac: rk3588: Allow multiple gmac controller") Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang Reviewed-by: Sebastian Reichel Reviewed-by: Ramon Fried --- drivers/net/dwc_eth_qos.c | 4 + drivers/net/dwc_eth_qos_rockchip.c | 182 ++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 690e8a34af6..18466cfe257 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1713,6 +1713,10 @@ static const struct udevice_id eqos_ids[] = { .compatible = "rockchip,rk3568-gmac", .data = (ulong)&eqos_rockchip_config }, + { + .compatible = "rockchip,rk3588-gmac", + .data = (ulong)&eqos_rockchip_config + }, #endif #if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM) { diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c index 9d57e8db764..834307a4477 100644 --- a/drivers/net/dwc_eth_qos_rockchip.c +++ b/drivers/net/dwc_eth_qos_rockchip.c @@ -28,6 +28,7 @@ struct rk_gmac_ops { int tx_delay, int rx_delay); int (*set_to_rmii)(struct udevice *dev); int (*set_gmac_speed)(struct udevice *dev); + void (*set_clock_selection)(struct udevice *dev, bool enable); u32 regs[3]; }; @@ -35,7 +36,9 @@ struct rockchip_platform_data { struct reset_ctl_bulk resets; const struct rk_gmac_ops *ops; int id; + bool clock_input; struct regmap *grf; + struct regmap *php_grf; }; #define HIWORD_UPDATE(val, mask, shift) \ @@ -129,6 +132,137 @@ static int rk3568_set_gmac_speed(struct udevice *dev) return 0; } +/* sys_grf */ +#define RK3588_GRF_GMAC_CON7 0x031c +#define RK3588_GRF_GMAC_CON8 0x0320 +#define RK3588_GRF_GMAC_CON9 0x0324 + +#define RK3588_GMAC_RXCLK_DLY_ENABLE(id) GRF_BIT(2 * (id) + 3) +#define RK3588_GMAC_RXCLK_DLY_DISABLE(id) GRF_CLR_BIT(2 * (id) + 3) +#define RK3588_GMAC_TXCLK_DLY_ENABLE(id) GRF_BIT(2 * (id) + 2) +#define RK3588_GMAC_TXCLK_DLY_DISABLE(id) GRF_CLR_BIT(2 * (id) + 2) + +#define RK3588_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8) +#define RK3588_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0) + +/* php_grf */ +#define RK3588_GRF_GMAC_CON0 0x0008 +#define RK3588_GRF_CLK_CON1 0x0070 + +#define RK3588_GMAC_PHY_INTF_SEL_RGMII(id) \ + (GRF_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_CLR_BIT(5 + (id) * 6)) +#define RK3588_GMAC_PHY_INTF_SEL_RMII(id) \ + (GRF_CLR_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_BIT(5 + (id) * 6)) + +#define RK3588_GMAC_CLK_RMII_MODE(id) GRF_BIT(5 * (id)) +#define RK3588_GMAC_CLK_RGMII_MODE(id) GRF_CLR_BIT(5 * (id)) + +#define RK3588_GMAC_CLK_SELET_CRU(id) GRF_BIT(5 * (id) + 4) +#define RK3588_GMAC_CLK_SELET_IO(id) GRF_CLR_BIT(5 * (id) + 4) + +#define RK3588_GMAC_CLK_RMII_DIV2(id) GRF_BIT(5 * (id) + 2) +#define RK3588_GMAC_CLK_RMII_DIV20(id) GRF_CLR_BIT(5 * (id) + 2) + +#define RK3588_GMAC_CLK_RGMII_DIV1(id) \ + (GRF_CLR_BIT(5 * (id) + 2) | GRF_CLR_BIT(5 * (id) + 3)) +#define RK3588_GMAC_CLK_RGMII_DIV5(id) \ + (GRF_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3)) +#define RK3588_GMAC_CLK_RGMII_DIV50(id) \ + (GRF_CLR_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3)) + +#define RK3588_GMAC_CLK_RMII_GATE(id) GRF_BIT(5 * (id) + 1) +#define RK3588_GMAC_CLK_RMII_NOGATE(id) GRF_CLR_BIT(5 * (id) + 1) + +static int rk3588_set_to_rgmii(struct udevice *dev, + int tx_delay, int rx_delay) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 offset_con, id = data->id; + + offset_con = data->id == 1 ? RK3588_GRF_GMAC_CON9 : + RK3588_GRF_GMAC_CON8; + + regmap_write(data->php_grf, RK3588_GRF_GMAC_CON0, + RK3588_GMAC_PHY_INTF_SEL_RGMII(id)); + + regmap_write(data->php_grf, RK3588_GRF_CLK_CON1, + RK3588_GMAC_CLK_RGMII_MODE(id)); + + regmap_write(data->grf, RK3588_GRF_GMAC_CON7, + RK3588_GMAC_RXCLK_DLY_ENABLE(id) | + RK3588_GMAC_TXCLK_DLY_ENABLE(id)); + + regmap_write(data->grf, offset_con, + RK3588_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3588_GMAC_CLK_TX_DL_CFG(tx_delay)); + + return 0; +} + +static int rk3588_set_to_rmii(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + regmap_write(data->php_grf, RK3588_GRF_GMAC_CON0, + RK3588_GMAC_PHY_INTF_SEL_RMII(data->id)); + + regmap_write(data->php_grf, RK3588_GRF_CLK_CON1, + RK3588_GMAC_CLK_RMII_MODE(data->id)); + + return 0; +} + +static int rk3588_set_gmac_speed(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + u32 val = 0, id = data->id; + + switch (eqos->phy->speed) { + case SPEED_10: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = RK3588_GMAC_CLK_RMII_DIV20(id); + else + val = RK3588_GMAC_CLK_RGMII_DIV50(id); + break; + case SPEED_100: + if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) + val = RK3588_GMAC_CLK_RMII_DIV2(id); + else + val = RK3588_GMAC_CLK_RGMII_DIV5(id); + break; + case SPEED_1000: + if (pdata->phy_interface != PHY_INTERFACE_MODE_RMII) + val = RK3588_GMAC_CLK_RGMII_DIV1(id); + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + regmap_write(data->php_grf, RK3588_GRF_CLK_CON1, val); + + return 0; +} + +static void rk3588_set_clock_selection(struct udevice *dev, bool enable) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + u32 val = data->clock_input ? RK3588_GMAC_CLK_SELET_IO(data->id) : + RK3588_GMAC_CLK_SELET_CRU(data->id); + + val |= enable ? RK3588_GMAC_CLK_RMII_NOGATE(data->id) : + RK3588_GMAC_CLK_RMII_GATE(data->id); + + regmap_write(data->php_grf, RK3588_GRF_CLK_CON1, val); +} + static const struct rk_gmac_ops rk_gmac_ops[] = { { .compatible = "rockchip,rk3568-gmac", @@ -141,6 +275,18 @@ static const struct rk_gmac_ops rk_gmac_ops[] = { 0x0, /* sentinel */ }, }, + { + .compatible = "rockchip,rk3588-gmac", + .set_to_rgmii = rk3588_set_to_rgmii, + .set_to_rmii = rk3588_set_to_rmii, + .set_gmac_speed = rk3588_set_gmac_speed, + .set_clock_selection = rk3588_set_clock_selection, + .regs = { + 0xfe1b0000, /* gmac0 */ + 0xfe1c0000, /* gmac1 */ + 0x0, /* sentinel */ + }, + }, { } }; @@ -162,6 +308,7 @@ static int eqos_probe_resources_rk(struct udevice *dev) struct eqos_priv *eqos = dev_get_priv(dev); struct eth_pdata *pdata = dev_get_plat(dev); struct rockchip_platform_data *data; + const char *clock_in_out; int reset_flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE; int ret; @@ -199,6 +346,16 @@ static int eqos_probe_resources_rk(struct udevice *dev) goto err_free; } + if (device_is_compatible(dev, "rockchip,rk3588-gmac")) { + data->php_grf = + syscon_regmap_lookup_by_phandle(dev, "rockchip,php-grf"); + if (IS_ERR(data->php_grf)) { + dev_err(dev, "Missing rockchip,php-grf property\n"); + ret = -EINVAL; + goto err_free; + } + } + ret = reset_get_bulk(dev, &data->resets); if (ret < 0) goto err_free; @@ -211,12 +368,20 @@ static int eqos_probe_resources_rk(struct udevice *dev) goto err_release_resets; } - ret = clk_get_by_name(dev, "clk_mac_speed", &eqos->clk_tx); - if (ret) { - dev_dbg(dev, "clk_get_by_name(clk_mac_speed) failed: %d", ret); - goto err_free_clk_master_bus; + if (device_is_compatible(dev, "rockchip,rk3568-gmac")) { + ret = clk_get_by_name(dev, "clk_mac_speed", &eqos->clk_tx); + if (ret) { + dev_dbg(dev, "clk_get_by_name(clk_mac_speed) failed: %d", ret); + goto err_free_clk_master_bus; + } } + clock_in_out = dev_read_string(dev, "clock_in_out"); + if (clock_in_out && !strcmp(clock_in_out, "input")) + data->clock_input = true; + else + data->clock_input = false; + /* snps,reset props are deprecated, do bare minimum to support them */ if (dev_read_bool(dev, "snps,reset-active-low")) reset_flags |= GPIOD_ACTIVE_LOW; @@ -273,6 +438,12 @@ static int eqos_start_resets_rk(struct udevice *dev) static int eqos_stop_clks_rk(struct udevice *dev) { + struct eth_pdata *pdata = dev_get_plat(dev); + struct rockchip_platform_data *data = pdata->priv_pdata; + + if (data->ops->set_clock_selection) + data->ops->set_clock_selection(dev, false); + return 0; } @@ -293,6 +464,9 @@ static int eqos_start_clks_rk(struct udevice *dev) udelay(eqos->reset_delays[2]); } + if (data->ops->set_clock_selection) + data->ops->set_clock_selection(dev, true); + tx_delay = dev_read_u32_default(dev, "tx_delay", 0x30); rx_delay = dev_read_u32_default(dev, "rx_delay", 0x10); -- cgit v1.3.1 From c12e3a83f16f4965c4fbbbb120d53be5e487e2ef Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Thu, 17 Aug 2023 05:45:02 +0000 Subject: power: pmic: rk8xx: Use sysreset implementation of the poweroff command Select SYSRESET_CMD_POWEROFF to use the sysreset implementation of the poweroff command when PMIC_RK8XX is enabled. Signed-off-by: Jonas Karlman Reviewed-by: Simon Glass Reviewed-by: Kever Yang --- drivers/power/pmic/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 176fb07c651..4a6f0ce093a 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -233,6 +233,7 @@ config PMIC_QCOM config PMIC_RK8XX bool "Enable support for Rockchip PMIC RK8XX" + select SYSRESET_CMD_POWEROFF if SYSRESET && CMD_POWEROFF ---help--- The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs, an RTC and two low Rds (resistance (drain to source)) switches. It is -- cgit v1.3.1 From c8ebcc921f6c4535746df843762ff80c2c0b8bf2 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Thu, 3 Aug 2023 21:02:42 +0000 Subject: power: pmic: rk8xx: Fix power-on source check in SPL The commit 30975fb73d51 ("rockchip: Add option to prevent booting on power plug-in") introduce an option to prevent booting a device when the device was powered on due to power plug-in instead of pressing a power button. This feature works by checking the power-on source during PMIC probe and powers off the device if power-on source was power plug-in. This check currently runs very late at PMIC probe in U-Boot proper. Fix so that the power-on source check can work at probe time in SPL. Also enable probe after bind and remove the PMIC banner in SPL. With this we can use ROCKCHIP_RK8XX_DISABLE_BOOT_ON_POWERON and SPL_PMIC_RK8XX to power off the device very quickly after TPL instead of after TF-A and U-Boot proper has been loaded and run. DDR V1.18 f366f69a7d typ 23/07/17-15:48:58 ln LP4/4x derate en, other dram:1x trefi ddrconfig:7 LPDDR4X, 324MHz BW=32 Col=10 Bk=8 CS0 Row=17 CS1 Row=17 CS=2 Die BW=16 Size=8192MB change to: 324MHz clk skew:0x64 change to: 528MHz clk skew:0x58 change to: 780MHz clk skew:0x58 change to: 1056MHz(final freq) clk skew:0x40 out Power Off due to plug-in event Fixes: 30975fb73d51 ("rockchip: Add option to prevent booting on power plug-in") Signed-off-by: Jonas Karlman --- drivers/power/pmic/rk8xx.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c index 25ef621f8df..4e3a17337ee 100644 --- a/drivers/power/pmic/rk8xx.c +++ b/drivers/power/pmic/rk8xx.c @@ -156,6 +156,10 @@ static int rk8xx_bind(struct udevice *dev) if (!children) debug("%s: %s - no child found\n", __func__, dev->name); + if (IS_ENABLED(CONFIG_SPL_BUILD) && + IS_ENABLED(CONFIG_ROCKCHIP_RK8XX_DISABLE_BOOT_ON_POWERON)) + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); + /* Always return success for this device */ return 0; } @@ -236,14 +240,16 @@ static int rk8xx_probe(struct udevice *dev) pmic_reg_read(dev, init_data[i].reg)); } - printf("PMIC: RK%x ", show_variant); + if (!IS_ENABLED(CONFIG_SPL_BUILD)) { + printf("PMIC: RK%x ", show_variant); + if (on_source && off_source) + printf("(on=0x%02x, off=0x%02x)", + pmic_reg_read(dev, on_source), + pmic_reg_read(dev, off_source)); + printf("\n"); + } - if (on_source && off_source) - printf("(on=0x%02x, off=0x%02x)", - pmic_reg_read(dev, on_source), - pmic_reg_read(dev, off_source)); - printf("\n"); - if (CONFIG_IS_ENABLED(ROCKCHIP_RK8XX_DISABLE_BOOT_ON_POWERON)) + if (IS_ENABLED(CONFIG_ROCKCHIP_RK8XX_DISABLE_BOOT_ON_POWERON)) rk8xx_off_for_plugin(dev); return 0; -- cgit v1.3.1