From ea0f7662531fd360abf300691c85ceff5a0d0397 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Tue, 17 Jan 2023 17:07:47 +0000 Subject: mmc: rockchip_dw_mmc: fix DDR52 8-bit mode handling The RK3288 TRM states that, for 8-bit DDR modes: The CLKDIV register should always be programmed with a value higher than zero (0); that is, a clock divider should always be used for 8-bit DDR mode. In Linux, the driver applies this logic for all SoCs using the driver and does not distinguish RK3288, so presumably this requirement is the same for all other Rockchip SoCs using this IP. Add the necessary code to double the clock frequency when 8-bit DDR is selected. The dw_mmc core already handles setting CLKDIV correctly given the input clock and desired bus clock. Signed-off-by: John Keeping Reviewed-by: Jaehoon Chung --- drivers/mmc/rockchip_dw_mmc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 573bf16c875..3661ce33143 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -41,6 +41,14 @@ static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); int ret; + /* + * The clock frequency chosen here affects CLKDIV in the dw_mmc core. + * That can be either 0 or 1, but it must be set to 1 for eMMC DDR52 + * 8-bit mode. It will be set to 0 for all other modes. + */ + if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8) + freq *= 2; + ret = clk_set_rate(&priv->clk, freq); if (ret < 0) { debug("%s: err=%d\n", __func__, ret); -- cgit v1.2.3 From d538efb9adcfa28e238c26146f58e040b0ffdc5b Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Fri, 17 Feb 2023 17:28:39 +0530 Subject: phy: rockchip: inno-usb2: Add support #address_cells = 2 New Rockchip devices have the usb phy nodes as standalone devices. These nodes have register nodes with #address_cells = 2, but only use 32 bit addresses. Adjust the driver to check if the returned address is "0", and adjust the index in that case. Derived and adjusted the similar change from linux-next with below commit <9c19c531dc98> ("phy: phy-rockchip-inno-usb2: support #address_cells = 2") Co-developed-by: Manoj Sai Signed-off-by: Manoj Sai Signed-off-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index b32a498ea71..a01148db220 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -179,12 +179,21 @@ static int rockchip_usb2phy_probe(struct udevice *dev) if (IS_ERR(priv->reg_base)) return PTR_ERR(priv->reg_base); - ret = ofnode_read_u32(dev_ofnode(dev), "reg", ®); + ret = ofnode_read_u32_index(dev_ofnode(dev), "reg", 0, ®); if (ret) { dev_err(dev, "failed to read reg property (ret = %d)\n", ret); return ret; } + /* support address_cells=2 */ + if (reg == 0) { + if (ofnode_read_u32_index(dev_ofnode(dev), "reg", 1, ®)) { + dev_err(dev, "%s must have reg[1]\n", + ofnode_get_name(dev_ofnode(dev))); + return -EINVAL; + } + } + phy_cfgs = (const struct rockchip_usb2phy_cfg *) dev_get_driver_data(dev); if (!phy_cfgs) -- cgit v1.2.3 From 3da15f0b49a22743b6ed5756e4082287a384bc83 Mon Sep 17 00:00:00 2001 From: Manoj Sai Date: Fri, 17 Feb 2023 17:28:40 +0530 Subject: phy: rockchip-inno-usb2: Add USB2 PHY for rk3568 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RK3568 has two USB 2.0 PHYs, and each PHY has two ports, the OTG port of PHY0 support OTG mode with charging detection function, they are similar to previous Rockchip SoCs. However, there are three different designs for RK3568 USB 2.0 PHY. 1. RK3568 uses independent USB GRF module for each USB 2.0 PHY. 2. RK3568 accesses the registers of USB 2.0 PHY IP directly by APB. 3. The two ports of USB 2.0 PHY share one interrupt. This patch only PHY1 with necessary attributes required to function USBPHY1 on U-Boot. Co-developed-by: Ren Jianing Signed-off-by: Ren Jianing Co-developed-by: Jagan Teki Signed-off-by: Jagan Teki Signed-off-by: Manoj Sai Reviewed-by: Kever Yang --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index a01148db220..55e1dbcfef7 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -298,11 +298,65 @@ static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8a0000, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0x052, 0x1d1 }, + .bvalid_det_en = { 0x0080, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x0084, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x0088, 2, 2, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .utmi_avalid = { 0x00c0, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 }, + .utmi_ls = { 0x00c0, 5, 4, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + }, + { + .reg = 0xfe8b0000, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .utmi_ls = { 0x00c0, 5, 4, 0, 1 }, + .utmi_hstdet = { 0x00c0, 7, 7, 0, 1 } + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + }, + { /* sentinel */ } +}; + static const struct udevice_id rockchip_usb2phy_ids[] = { { .compatible = "rockchip,rk3399-usb2phy", .data = (ulong)&rk3399_usb2phy_cfgs, }, + { + .compatible = "rockchip,rk3568-usb2phy", + .data = (ulong)&rk3568_phy_cfgs, + }, { /* sentinel */ } }; -- cgit v1.2.3 From 82220526ac9887c39d2d5caa567a20378b3122b7 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Fri, 17 Feb 2023 17:28:41 +0530 Subject: drivers: phy: add naneng combphy for rk3568 RK3568 has three combo phys, and PCIe/USB3/SATA/QSGMII controllers share one pipe interface for each combo phy, here is the diagram of the complex connection. +----------------+ | | +------+ | USB3 OTG CTRL0 |---->| | | | | | +------------+ +----------------+ | PIPE | | | | MUX |---->| Combo PHY0 | +----------------+ | | | | | | | | +------------+ | SATA CTRL0 |---->| | | | +------+ +----------------+ +----------------+ | | +------+ | USB3 HOST CTRL1|---->| | | | | | +------------+ +----------------+ | PIPE | | | | MUX |---->| Combo PHY1 | +----------------+ | | | | | |---->| | +------------+ | SATA CTRL1 | -->| | | | | +------+ +----------------+ | | +----------------+ | | | | +------+ | QSGMII CTRL |---->| | | | | | +------------+ +----------------+ | PIPE | | | | MUX |---->| Combo PHY2 | +----------------+ | | | | | |---->| | +------------+ | SATA CTRL2 | -->| | | | | +------+ +----------------+ | | +----------------+ | | | | | PCIe2 1-Lane |--- | | +----------------+ Co-developed-by: Manoj Sai Signed-off-by: Manoj Sai Co-developed-by: Yifeng Zhao Signed-off-by: Yifeng Zhao Signed-off-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/phy/rockchip/Kconfig | 7 + drivers/phy/rockchip/Makefile | 1 + drivers/phy/rockchip/phy-rockchip-naneng-combphy.c | 441 +++++++++++++++++++++ 3 files changed, 449 insertions(+) create mode 100644 drivers/phy/rockchip/phy-rockchip-naneng-combphy.c (limited to 'drivers') diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index e477a6cd9e9..13057639403 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -11,6 +11,13 @@ config PHY_ROCKCHIP_INNO_USB2 help Support for Rockchip USB2.0 PHY with Innosilicon IP block. +config PHY_ROCKCHIP_NANENG_COMBOPHY + bool "Support Rockchip NANENG combo PHY Driver" + depends on ARCH_ROCKCHIP + select PHY + help + Enable this to support the Rockchip NANENG combo PHY. + config PHY_ROCKCHIP_PCIE bool "Rockchip PCIe PHY Driver" depends on ARCH_ROCKCHIP diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index f6ad3bf59ae..a236877234b 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY) += phy-rockchip-naneng-combphy.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c new file mode 100644 index 00000000000..78da5fe7970 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip USB3.0/PCIe Gen2/SATA/SGMII combphy driver + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BIT_WRITEABLE_SHIFT 16 + +struct rockchip_combphy_priv; + +struct combphy_reg { + u16 offset; + u16 bitend; + u16 bitstart; + u16 disable; + u16 enable; +}; + +struct rockchip_combphy_grfcfg { + struct combphy_reg pcie_mode_set; + struct combphy_reg usb_mode_set; + struct combphy_reg sgmii_mode_set; + struct combphy_reg qsgmii_mode_set; + struct combphy_reg pipe_rxterm_set; + struct combphy_reg pipe_txelec_set; + struct combphy_reg pipe_txcomp_set; + struct combphy_reg pipe_clk_25m; + struct combphy_reg pipe_clk_100m; + struct combphy_reg pipe_phymode_sel; + struct combphy_reg pipe_rate_sel; + struct combphy_reg pipe_rxterm_sel; + struct combphy_reg pipe_txelec_sel; + struct combphy_reg pipe_txcomp_sel; + struct combphy_reg pipe_clk_ext; + struct combphy_reg pipe_sel_usb; + struct combphy_reg pipe_sel_qsgmii; + struct combphy_reg pipe_phy_status; + struct combphy_reg con0_for_pcie; + struct combphy_reg con1_for_pcie; + struct combphy_reg con2_for_pcie; + struct combphy_reg con3_for_pcie; + struct combphy_reg con0_for_sata; + struct combphy_reg con1_for_sata; + struct combphy_reg con2_for_sata; + struct combphy_reg con3_for_sata; + struct combphy_reg pipe_con0_for_sata; + struct combphy_reg pipe_sgmii_mac_sel; + struct combphy_reg pipe_xpcs_phy_ready; + struct combphy_reg u3otg0_port_en; + struct combphy_reg u3otg1_port_en; +}; + +struct rockchip_combphy_cfg { + const struct rockchip_combphy_grfcfg *grfcfg; + int (*combphy_cfg)(struct rockchip_combphy_priv *priv); +}; + +struct rockchip_combphy_priv { + u32 mode; + void __iomem *mmio; + struct udevice *dev; + struct regmap *pipe_grf; + struct regmap *phy_grf; + struct phy *phy; + struct reset_ctl phy_rst; + struct clk ref_clk; + const struct rockchip_combphy_cfg *cfg; +}; + +static int param_write(struct regmap *base, + const struct combphy_reg *reg, bool en) +{ + u32 val, mask, tmp; + + tmp = en ? reg->enable : reg->disable; + mask = GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + + return regmap_write(base, reg->offset, val); +} + +static int rockchip_combphy_pcie_init(struct rockchip_combphy_priv *priv) +{ + int ret = 0; + + if (priv->cfg->combphy_cfg) { + ret = priv->cfg->combphy_cfg(priv); + if (ret) { + dev_err(priv->dev, "failed to init phy for pcie\n"); + return ret; + } + } + + return ret; +} + +static int rockchip_combphy_usb3_init(struct rockchip_combphy_priv *priv) +{ + int ret = 0; + + if (priv->cfg->combphy_cfg) { + ret = priv->cfg->combphy_cfg(priv); + if (ret) { + dev_err(priv->dev, "failed to init phy for usb3\n"); + return ret; + } + } + + return ret; +} + +static int rockchip_combphy_sata_init(struct rockchip_combphy_priv *priv) +{ + int ret = 0; + + if (priv->cfg->combphy_cfg) { + ret = priv->cfg->combphy_cfg(priv); + if (ret) { + dev_err(priv->dev, "failed to init phy for sata\n"); + return ret; + } + } + + return ret; +} + +static int rockchip_combphy_sgmii_init(struct rockchip_combphy_priv *priv) +{ + int ret = 0; + + if (priv->cfg->combphy_cfg) { + ret = priv->cfg->combphy_cfg(priv); + if (ret) { + dev_err(priv->dev, "failed to init phy for sgmii\n"); + return ret; + } + } + + return ret; +} + +static int rockchip_combphy_set_mode(struct rockchip_combphy_priv *priv) +{ + switch (priv->mode) { + case PHY_TYPE_PCIE: + rockchip_combphy_pcie_init(priv); + break; + case PHY_TYPE_USB3: + rockchip_combphy_usb3_init(priv); + break; + case PHY_TYPE_SATA: + rockchip_combphy_sata_init(priv); + break; + case PHY_TYPE_SGMII: + case PHY_TYPE_QSGMII: + return rockchip_combphy_sgmii_init(priv); + default: + dev_err(priv->dev, "incompatible PHY type\n"); + return -EINVAL; + } + + return 0; +} + +static int rockchip_combphy_init(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = dev_get_priv(phy->dev); + int ret; + + ret = clk_enable(&priv->ref_clk); + if (ret < 0 && ret != -ENOSYS) + return ret; + + ret = rockchip_combphy_set_mode(priv); + if (ret) + goto err_clk; + + reset_deassert(&priv->phy_rst); + + return 0; + +err_clk: + clk_disable(&priv->ref_clk); + + return ret; +} + +static int rockchip_combphy_exit(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = dev_get_priv(phy->dev); + + clk_disable(&priv->ref_clk); + reset_assert(&priv->phy_rst); + + return 0; +} + +static int rockchip_combphy_xlate(struct phy *phy, struct ofnode_phandle_args *args) +{ + struct rockchip_combphy_priv *priv = dev_get_priv(phy->dev); + + if (args->args_count != 1) { + pr_err("invalid number of arguments\n"); + return -EINVAL; + } + + priv->mode = args->args[0]; + + return 0; +} + +static const struct phy_ops rochchip_combphy_ops = { + .init = rockchip_combphy_init, + .exit = rockchip_combphy_exit, + .of_xlate = rockchip_combphy_xlate, +}; + +static int rockchip_combphy_parse_dt(struct udevice *dev, + struct rockchip_combphy_priv *priv) +{ + struct udevice *syscon; + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,pipe-grf", &syscon); + if (ret) { + dev_err(dev, "failed to find peri_ctrl pipe-grf regmap"); + return ret; + } + priv->pipe_grf = syscon_get_regmap(syscon); + + ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,pipe-phy-grf", &syscon); + if (ret) { + dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n"); + return ret; + } + priv->phy_grf = syscon_get_regmap(syscon); + + ret = clk_get_by_index(dev, 0, &priv->ref_clk); + if (ret) { + dev_err(dev, "failed to find ref clock\n"); + return PTR_ERR(&priv->ref_clk); + } + + ret = reset_get_by_index(dev, 0, &priv->phy_rst); + if (ret) { + dev_err(dev, "no phy reset control specified\n"); + return ret; + } + + return 0; +} + +static int rockchip_combphy_probe(struct udevice *udev) +{ + struct rockchip_combphy_priv *priv = dev_get_priv(udev); + const struct rockchip_combphy_cfg *phy_cfg; + + priv->mmio = (void __iomem *)dev_read_addr(udev); + if (IS_ERR(priv->mmio)) + return PTR_ERR(priv->mmio); + + phy_cfg = (const struct rockchip_combphy_cfg *)dev_get_driver_data(udev); + if (!phy_cfg) { + dev_err(udev, "No OF match data provided\n"); + return -EINVAL; + } + + priv->dev = udev; + priv->mode = PHY_TYPE_SATA; + priv->cfg = phy_cfg; + + return rockchip_combphy_parse_dt(udev, priv); +} + +static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + u32 val; + + switch (priv->mode) { + case PHY_TYPE_PCIE: + /* Set SSC downward spread spectrum */ + val = readl(priv->mmio + (0x1f << 2)); + val &= ~GENMASK(5, 4); + val |= 0x01 << 4; + writel(val, priv->mmio + 0x7c); + + param_write(priv->phy_grf, &cfg->con0_for_pcie, true); + 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); + break; + case PHY_TYPE_USB3: + /* Set SSC downward spread spectrum */ + val = readl(priv->mmio + (0x1f << 2)); + val &= ~GENMASK(5, 4); + val |= 0x01 << 4; + writel(val, priv->mmio + 0x7c); + + /* Enable adaptive CTLE for USB3.0 Rx */ + val = readl(priv->mmio + (0x0e << 2)); + val &= ~GENMASK(0, 0); + val |= 0x01; + writel(val, priv->mmio + (0x0e << 2)); + + /* Set PLL KVCO fine tuning signals */ + val = readl(priv->mmio + (0x20 << 2)); + val &= ~(0x7 << 2); + val |= 0x2 << 2; + writel(val, priv->mmio + (0x20 << 2)); + + /* Set PLL LPF R1 to su_trim[10:7]=1001 */ + writel(0x4, priv->mmio + (0xb << 2)); + + /* Set PLL input clock divider 1/2 */ + val = readl(priv->mmio + (0x5 << 2)); + val &= ~(0x3 << 6); + val |= 0x1 << 6; + writel(val, priv->mmio + (0x5 << 2)); + + /* Set PLL loop divider */ + writel(0x32, priv->mmio + (0x11 << 2)); + + /* Set PLL KVCO to min and set PLL charge pump current to max */ + writel(0xf0, priv->mmio + (0xa << 2)); + + param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); + param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); + param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); + param_write(priv->phy_grf, &cfg->usb_mode_set, true); + break; + case PHY_TYPE_SATA: + writel(0x41, priv->mmio + 0x38); + writel(0x8F, priv->mmio + 0x18); + param_write(priv->phy_grf, &cfg->con0_for_sata, true); + param_write(priv->phy_grf, &cfg->con1_for_sata, true); + param_write(priv->phy_grf, &cfg->con2_for_sata, true); + param_write(priv->phy_grf, &cfg->con3_for_sata, true); + param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); + break; + case PHY_TYPE_SGMII: + param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + param_write(priv->phy_grf, &cfg->sgmii_mode_set, true); + break; + case PHY_TYPE_QSGMII: + param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + param_write(priv->phy_grf, &cfg->pipe_rate_sel, true); + param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true); + break; + default: + pr_err("%s, phy-type %d\n", __func__, priv->mode); + return -EINVAL; + } + + /* The default ref clock is 25Mhz */ + param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); + + if (dev_read_bool(priv->dev, "rockchip,enable-ssc")) { + val = readl(priv->mmio + (0x7 << 2)); + val |= BIT(4); + writel(val, priv->mmio + (0x7 << 2)); + } + + return 0; +} + +static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 }, + .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 }, + .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 }, + .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 }, + .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 }, + .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 }, + .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 }, + .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 }, + .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 }, + .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 }, + .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 }, + /* pipe-grf */ + .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, + .pipe_sgmii_mac_sel = { 0x0040, 1, 1, 0x00, 0x01 }, + .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 }, + .u3otg0_port_en = { 0x0104, 15, 0, 0x0181, 0x1100 }, + .u3otg1_port_en = { 0x0144, 15, 0, 0x0181, 0x1100 }, +}; + +static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { + .grfcfg = &rk3568_combphy_grfcfgs, + .combphy_cfg = rk3568_combphy_cfg, +}; + +static const struct udevice_id rockchip_combphy_ids[] = { + { + .compatible = "rockchip,rk3568-naneng-combphy", + .data = (ulong)&rk3568_combphy_cfgs + }, + { } +}; + +U_BOOT_DRIVER(rockchip_naneng_combphy) = { + .name = "naneng-combphy", + .id = UCLASS_PHY, + .of_match = rockchip_combphy_ids, + .ops = &rochchip_combphy_ops, + .probe = rockchip_combphy_probe, + .priv_auto = sizeof(struct rockchip_combphy_priv), +}; -- cgit v1.2.3 From 1977d746aa54ae197a9d5f24414680d3ca321fb1 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Fri, 17 Feb 2023 17:28:43 +0530 Subject: rockchip: rk3568: add rk3568 pinctrl driver Add driver supporting pin multiplexing on rk3568 platform. Co-developed-by: Manoj Sai Signed-off-by: Manoj Sai Co-developed-by: Jianqun Xu Signed-off-by: Jianqun Xu Signed-off-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/pinctrl/rockchip/Makefile | 1 + drivers/pinctrl/rockchip/pinctrl-rk3568.c | 362 ++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 drivers/pinctrl/rockchip/pinctrl-rk3568.c (limited to 'drivers') diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile index 98843554733..90461ae8819 100644 --- a/drivers/pinctrl/rockchip/Makefile +++ b/drivers/pinctrl/rockchip/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += pinctrl-rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) += pinctrl-rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += pinctrl-rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += pinctrl-rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3568) += pinctrl-rk3568.o obj-$(CONFIG_ROCKCHIP_RV1108) += pinctrl-rv1108.o obj-$(CONFIG_ROCKCHIP_RV1126) += pinctrl-rv1126.o diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3568.c b/drivers/pinctrl/rockchip/pinctrl-rk3568.c new file mode 100644 index 00000000000..935aed9efc6 --- /dev/null +++ b/drivers/pinctrl/rockchip/pinctrl-rk3568.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#include + +#include "pinctrl-rockchip.h" + +static struct rockchip_mux_route_data rk3568_mux_route_data[] = { + MR_TOPGRF(RK_GPIO0, RK_PB3, RK_FUNC_2, 0x0300, RK_GENMASK_VAL(0, 0, 0)), /* CAN0 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PA1, RK_FUNC_4, 0x0300, RK_GENMASK_VAL(0, 0, 1)), /* CAN0 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA1, RK_FUNC_3, 0x0300, RK_GENMASK_VAL(2, 2, 0)), /* CAN1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PC3, RK_FUNC_3, 0x0300, RK_GENMASK_VAL(2, 2, 1)), /* CAN1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PB5, RK_FUNC_3, 0x0300, RK_GENMASK_VAL(4, 4, 0)), /* CAN2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PB2, RK_FUNC_4, 0x0300, RK_GENMASK_VAL(4, 4, 1)), /* CAN2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PC4, RK_FUNC_1, 0x0300, RK_GENMASK_VAL(6, 6, 0)), /* EDPDP_HPDIN IO mux selection M0 */ + MR_TOPGRF(RK_GPIO0, RK_PC2, RK_FUNC_2, 0x0300, RK_GENMASK_VAL(6, 6, 1)), /* EDPDP_HPDIN IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PB1, RK_FUNC_3, 0x0300, RK_GENMASK_VAL(8, 8, 0)), /* GMAC1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PA7, RK_FUNC_3, 0x0300, RK_GENMASK_VAL(8, 8, 1)), /* GMAC1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PD1, RK_FUNC_1, 0x0300, RK_GENMASK_VAL(10, 10, 0)), /* HDMITX IO mux selection M0 */ + MR_TOPGRF(RK_GPIO0, RK_PC7, RK_FUNC_1, 0x0300, RK_GENMASK_VAL(10, 10, 1)), /* HDMITX IO mux selection M1 */ + MR_TOPGRF(RK_GPIO0, RK_PB6, RK_FUNC_1, 0x0300, RK_GENMASK_VAL(14, 14, 0)), /* I2C2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PB4, RK_FUNC_1, 0x0300, RK_GENMASK_VAL(14, 14, 1)), /* I2C2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA0, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(0, 0, 0)), /* I2C3 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PB6, RK_FUNC_4, 0x0304, RK_GENMASK_VAL(0, 0, 1)), /* I2C3 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PB2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(2, 2, 0)), /* I2C4 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PB1, RK_FUNC_2, 0x0304, RK_GENMASK_VAL(2, 2, 1)), /* I2C4 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PB4, RK_FUNC_4, 0x0304, RK_GENMASK_VAL(4, 4, 0)), /* I2C5 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PD0, RK_FUNC_2, 0x0304, RK_GENMASK_VAL(4, 4, 1)), /* I2C5 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(6, 6, 0)), /* PWM4 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(6, 6, 1)), /* PWM4 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(8, 8, 0)), /* PWM5 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(8, 8, 1)), /* PWM5 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(10, 10, 0)), /* PWM6 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(10, 10, 1)), /* PWM6 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(12, 12, 0)), /* PWM7 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(12, 12, 1)), /* PWM7 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(14, 14, 0)), /* PWM8 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0304, RK_GENMASK_VAL(14, 14, 1)), /* PWM8 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(0, 0, 0)), /* PWM9 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(0, 0, 1)), /* PWM9 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(2, 2, 0)), /* PWM10 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(2, 2, 1)), /* PWM10 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(4, 4, 0)), /* PWM11 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(4, 4, 1)), /* PWM11 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(6, 6, 0)), /* PWM12 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(6, 6, 1)), /* PWM12 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(8, 8, 0)), /* PWM13 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(8, 8, 1)), /* PWM13 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(10, 10, 0)), /* PWM14 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(10, 10, 1)), /* PWM14 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(12, 12, 0)), /* PWM15 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0308, RK_GENMASK_VAL(12, 12, 1)), /* PWM15 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_3, 0x0308, RK_GENMASK_VAL(14, 14, 0)), /* SDMMC2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PA5, RK_FUNC_5, 0x0308, RK_GENMASK_VAL(14, 14, 1)), /* SDMMC2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO0, RK_PB5, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(0, 0, 0)), /* SPI0 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PD3, RK_FUNC_3, 0x030c, RK_GENMASK_VAL(0, 0, 1)), /* SPI0 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PB5, RK_FUNC_3, 0x030c, RK_GENMASK_VAL(2, 2, 0)), /* SPI1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PC3, RK_FUNC_3, 0x030c, RK_GENMASK_VAL(2, 2, 1)), /* SPI1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PC1, RK_FUNC_4, 0x030c, RK_GENMASK_VAL(4, 4, 0)), /* SPI2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PA0, RK_FUNC_3, 0x030c, RK_GENMASK_VAL(4, 4, 1)), /* SPI2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PB3, RK_FUNC_4, 0x030c, RK_GENMASK_VAL(6, 6, 0)), /* SPI3 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PC2, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(6, 6, 1)), /* SPI3 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PB4, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(8, 8, 0)), /* UART1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO0, RK_PD1, RK_FUNC_1, 0x030c, RK_GENMASK_VAL(8, 8, 1)), /* UART1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO0, RK_PD1, RK_FUNC_1, 0x030c, RK_GENMASK_VAL(10, 10, 0)), /* UART2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO1, RK_PD5, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(10, 10, 1)), /* UART2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA1, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(12, 12, 0)), /* UART3 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PB7, RK_FUNC_4, 0x030c, RK_GENMASK_VAL(12, 12, 1)), /* UART3 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA6, RK_FUNC_2, 0x030c, RK_GENMASK_VAL(14, 14, 0)), /* UART4 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PB2, RK_FUNC_4, 0x030c, RK_GENMASK_VAL(14, 14, 1)), /* UART4 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PA2, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(0, 0, 0)), /* UART5 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PC2, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(0, 0, 1)), /* UART5 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PA4, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(2, 2, 0)), /* UART6 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO1, RK_PD5, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(2, 2, 1)), /* UART6 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PA6, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(5, 4, 0)), /* UART7 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PC4, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(5, 4, 1)), /* UART7 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PD2, RK_FUNC_1, 0x0310, RK_GENMASK_VAL(5, 4, 2)), /* UART7 IO mux selection M2 */ + MR_TOPGRF(RK_GPIO2, RK_PC5, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(6, 6, 0)), /* UART8 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PD7, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(6, 6, 1)), /* UART8 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PB0, RK_FUNC_3, 0x0310, RK_GENMASK_VAL(9, 8, 0)), /* UART9 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PC5, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(9, 8, 1)), /* UART9 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PA4, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(9, 8, 2)), /* UART9 IO mux selection M2 */ + MR_TOPGRF(RK_GPIO1, RK_PA2, RK_FUNC_1, 0x0310, RK_GENMASK_VAL(11, 10, 0)), /* I2S1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PC6, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(11, 10, 1)), /* I2S1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO2, RK_PD0, RK_FUNC_5, 0x0310, RK_GENMASK_VAL(11, 10, 2)), /* I2S1 IO mux selection M2 */ + MR_TOPGRF(RK_GPIO2, RK_PC1, RK_FUNC_1, 0x0310, RK_GENMASK_VAL(12, 12, 0)), /* I2S2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PB6, RK_FUNC_5, 0x0310, RK_GENMASK_VAL(12, 12, 1)), /* I2S2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO3, RK_PA2, RK_FUNC_4, 0x0310, RK_GENMASK_VAL(14, 14, 0)), /* I2S3 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO4, RK_PC2, RK_FUNC_5, 0x0310, RK_GENMASK_VAL(14, 14, 1)), /* I2S3 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA6, RK_FUNC_3, 0x0314, RK_GENMASK_VAL(0, 0, 0)), /* PDM IO mux selection M0 */ + MR_TOPGRF(RK_GPIO3, RK_PD6, RK_FUNC_5, 0x0314, RK_GENMASK_VAL(0, 0, 1)), /* PDM IO mux selection M1 */ + MR_TOPGRF(RK_GPIO0, RK_PA5, RK_FUNC_3, 0x0314, RK_GENMASK_VAL(3, 2, 0)), /* PCIE20 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PD0, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(3, 2, 1)), /* PCIE20 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PB0, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(3, 2, 2)), /* PCIE20 IO mux selection M2 */ + MR_TOPGRF(RK_GPIO0, RK_PA4, RK_FUNC_3, 0x0314, RK_GENMASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PD2, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(5, 4, 1)), /* PCIE30X1 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO1, RK_PA5, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(5, 4, 2)), /* PCIE30X1 IO mux selection M2 */ + MR_TOPGRF(RK_GPIO0, RK_PA6, RK_FUNC_2, 0x0314, RK_GENMASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux selection M0 */ + MR_TOPGRF(RK_GPIO2, RK_PD4, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(7, 6, 1)), /* PCIE30X2 IO mux selection M1 */ + MR_TOPGRF(RK_GPIO4, RK_PC2, RK_FUNC_4, 0x0314, RK_GENMASK_VAL(7, 6, 2)), /* PCIE30X2 IO mux selection M2 */ +}; + +static int rk3568_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl_priv *priv = bank->priv; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, ret, mask; + u8 bit; + u32 data; + + debug("setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux); + + if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + regmap = priv->regmap_pmu; + else + regmap = priv->regmap_base; + + reg = bank->iomux[iomux_num].offset; + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + mask = 0xf; + + data = (mask << (bit + 16)); + data |= (mux & mask) << bit; + ret = regmap_write(regmap, reg, data); + + return ret; +} + +#define RK3568_PULL_PMU_OFFSET 0x20 +#define RK3568_PULL_GRF_OFFSET 0x80 +#define RK3568_PULL_BITS_PER_PIN 2 +#define RK3568_PULL_PINS_PER_REG 8 +#define RK3568_PULL_BANK_STRIDE 0x10 + +static void rk3568_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *info = bank->priv; + + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + *reg = RK3568_PULL_PMU_OFFSET; + *reg += bank->bank_num * RK3568_PULL_BANK_STRIDE; + } else { + *regmap = info->regmap_base; + *reg = RK3568_PULL_GRF_OFFSET; + *reg += (bank->bank_num - 1) * RK3568_PULL_BANK_STRIDE; + } + + *reg += ((pin_num / RK3568_PULL_PINS_PER_REG) * 4); + *bit = (pin_num % RK3568_PULL_PINS_PER_REG); + *bit *= RK3568_PULL_BITS_PER_PIN; +} + +#define RK3568_DRV_PMU_OFFSET 0x70 +#define RK3568_DRV_GRF_OFFSET 0x200 +#define RK3568_DRV_BITS_PER_PIN 8 +#define RK3568_DRV_PINS_PER_REG 2 +#define RK3568_DRV_BANK_STRIDE 0x40 + +static void rk3568_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *info = bank->priv; + + /* The first 32 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + *reg = RK3568_DRV_PMU_OFFSET; + } else { + *regmap = info->regmap_base; + *reg = RK3568_DRV_GRF_OFFSET; + *reg += (bank->bank_num - 1) * RK3568_DRV_BANK_STRIDE; + } + + *reg += ((pin_num / RK3568_DRV_PINS_PER_REG) * 4); + *bit = (pin_num % RK3568_DRV_PINS_PER_REG); + *bit *= RK3568_DRV_BITS_PER_PIN; +} + +#define RK3568_SCHMITT_BITS_PER_PIN 2 +#define RK3568_SCHMITT_PINS_PER_REG 8 +#define RK3568_SCHMITT_BANK_STRIDE 0x10 +#define RK3568_SCHMITT_GRF_OFFSET 0xc0 +#define RK3568_SCHMITT_PMUGRF_OFFSET 0x30 + +static int rk3568_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl_priv *info = bank->priv; + + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + *reg = RK3568_SCHMITT_PMUGRF_OFFSET; + } else { + *regmap = info->regmap_base; + *reg = RK3568_SCHMITT_GRF_OFFSET; + *reg += (bank->bank_num - 1) * RK3568_SCHMITT_BANK_STRIDE; + } + + *reg += ((pin_num / RK3568_SCHMITT_PINS_PER_REG) * 4); + *bit = pin_num % RK3568_SCHMITT_PINS_PER_REG; + *bit *= RK3568_SCHMITT_BITS_PER_PIN; + + return 0; +} + +static int rk3568_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct regmap *regmap; + int reg, ret; + u8 bit, type; + u32 data; + + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return -ENOTSUPP; + + rk3568_calc_pull_reg_and_bit(bank, pin_num, ®map, ®, &bit); + type = bank->pull_type[pin_num / 8]; + ret = rockchip_translate_pull_value(type, pull); + if (ret < 0) { + debug("unsupported pull setting %d\n", pull); + return ret; + } + + /* enable the write to the equivalent lower bits */ + data = ((1 << ROCKCHIP_PULL_BITS_PER_PIN) - 1) << (bit + 16); + + data |= (ret << bit); + ret = regmap_write(regmap, reg, data); + + return ret; +} + +static int rk3568_set_drive(struct rockchip_pin_bank *bank, + int pin_num, int strength) +{ + struct regmap *regmap; + int reg; + u32 data; + u8 bit; + int drv = (1 << (strength + 1)) - 1; + int ret = 0; + + rk3568_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data |= (drv << bit); + + ret = regmap_write(regmap, reg, data); + if (ret) + return ret; + + if (bank->bank_num == 1 && pin_num == 21) + reg = 0x0840; + else if (bank->bank_num == 2 && pin_num == 2) + reg = 0x0844; + else if (bank->bank_num == 2 && pin_num == 8) + reg = 0x0848; + else if (bank->bank_num == 3 && pin_num == 0) + reg = 0x084c; + else if (bank->bank_num == 3 && pin_num == 6) + reg = 0x0850; + else if (bank->bank_num == 4 && pin_num == 0) + reg = 0x0854; + else + return 0; + + data = ((1 << RK3568_DRV_BITS_PER_PIN) - 1) << 16; + data |= drv; + + return regmap_write(regmap, reg, data); +} + +static int rk3568_set_schmitt(struct rockchip_pin_bank *bank, + int pin_num, int enable) +{ + struct regmap *regmap; + int reg; + u32 data; + u8 bit; + + rk3568_calc_schmitt_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3568_SCHMITT_BITS_PER_PIN) - 1) << (bit + 16); + data |= (enable << bit); + + return regmap_write(regmap, reg, data); +} + +static struct rockchip_pin_bank rk3568_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, + IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, + IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT, + IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT), +}; + +static const struct rockchip_pin_ctrl rk3568_pin_ctrl = { + .pin_banks = rk3568_pin_banks, + .nr_banks = ARRAY_SIZE(rk3568_pin_banks), + .nr_pins = 160, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .iomux_routes = rk3568_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3568_mux_route_data), + .set_mux = rk3568_set_mux, + .set_pull = rk3568_set_pull, + .set_drive = rk3568_set_drive, + .set_schmitt = rk3568_set_schmitt, +}; + +static const struct udevice_id rk3568_pinctrl_ids[] = { + { + .compatible = "rockchip,rk3568-pinctrl", + .data = (ulong)&rk3568_pin_ctrl + }, + { } +}; + +U_BOOT_DRIVER(pinctrl_rk3568) = { + .name = "rockchip_rk3568_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = rk3568_pinctrl_ids, + .priv_auto = sizeof(struct rockchip_pinctrl_priv), + .ops = &rockchip_pinctrl_ops, +#if CONFIG_IS_ENABLED(OF_REAL) + .bind = dm_scan_fdt_dev, +#endif + .probe = rockchip_pinctrl_probe, +}; -- cgit v1.2.3 From 904b8700f81cbc6a49c4f693744a4d2c6c393d6d Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Mon, 13 Feb 2023 16:27:34 -0600 Subject: gpio: gpio-rockchip: parse gpio-ranges for bank id Use the new devicetree property of gpio-ranges to determine the GPIO bank ID. Preserve the "old" way of doing things too, so that boards can be migrated and tested gradually (I only have a 3566 and 3326 to test). Signed-off-by: Chris Morgan Reviewed-by: Kever Yang --- drivers/gpio/rk_gpio.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c index 68f30157a9a..f7ad4d68b45 100644 --- a/drivers/gpio/rk_gpio.c +++ b/drivers/gpio/rk_gpio.c @@ -142,6 +142,7 @@ static int rockchip_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; char *end; int ret; @@ -150,9 +151,22 @@ static int rockchip_gpio_probe(struct udevice *dev) if (ret) return ret; - uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK; - end = strrchr(dev->name, '@'); - priv->bank = trailing_strtoln(dev->name, end); + /* + * If "gpio-ranges" is present in the devicetree use it to parse + * the GPIO bank ID, otherwise use the legacy method. + */ + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), + "gpio-ranges", NULL, 3, + 0, &args); + if (!ret || ret != -ENOENT) { + uc_priv->gpio_count = args.args[2]; + priv->bank = args.args[1] / args.args[2]; + } else { + uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK; + end = strrchr(dev->name, '@'); + priv->bank = trailing_strtoln(dev->name, end); + } + priv->name[0] = 'A' + priv->bank; uc_priv->bank_name = priv->name; -- cgit v1.2.3 From 7a474df740237aa0be34799dbd62db8425a45930 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Jan 2023 20:27:36 +0530 Subject: clk: rockchip: Add rk3588 clk support Add clock driver support for Rockchip RK3588 SoC. Signed-off-by: Elaine Zhang Signed-off-by: Jagan Teki Reviewed-by: Kever Yang Signed-off-by: Kever Yang --- drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1997 insertions(+) create mode 100644 drivers/clk/rockchip/clk_rk3588.c (limited to 'drivers') diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f719f4e3791..9e379cc2e3b 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c new file mode 100644 index 00000000000..5271d943483 --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3588.c @@ -0,0 +1,1996 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd + * Author: Elaine Zhang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +static struct rockchip_pll_rate_table rk3588_pll_rates[] = { + /* _mhz, _p, _m, _s, _k */ + RK3588_PLL_RATE(1500000000, 2, 250, 1, 0), + RK3588_PLL_RATE(1200000000, 2, 200, 1, 0), + RK3588_PLL_RATE(1188000000, 2, 198, 1, 0), + RK3588_PLL_RATE(1100000000, 3, 550, 2, 0), + RK3588_PLL_RATE(1008000000, 2, 336, 2, 0), + RK3588_PLL_RATE(1000000000, 3, 500, 2, 0), + RK3588_PLL_RATE(900000000, 2, 300, 2, 0), + RK3588_PLL_RATE(850000000, 3, 425, 2, 0), + RK3588_PLL_RATE(816000000, 2, 272, 2, 0), + RK3588_PLL_RATE(786432000, 2, 262, 2, 9437), + RK3588_PLL_RATE(786000000, 1, 131, 2, 0), + RK3588_PLL_RATE(722534400, 8, 963, 2, 24850), + RK3588_PLL_RATE(600000000, 2, 200, 2, 0), + RK3588_PLL_RATE(594000000, 2, 198, 2, 0), + RK3588_PLL_RATE(200000000, 3, 400, 4, 0), + RK3588_PLL_RATE(100000000, 3, 400, 5, 0), + { /* sentinel */ }, +}; + +static struct rockchip_pll_clock rk3588_pll_clks[] = { + [B0PLL] = PLL(pll_rk3588, PLL_B0PLL, RK3588_B0_PLL_CON(0), + RK3588_B0_PLL_MODE_CON, 0, 15, 0, + rk3588_pll_rates), + [B1PLL] = PLL(pll_rk3588, PLL_B1PLL, RK3588_B1_PLL_CON(8), + RK3588_B1_PLL_MODE_CON, 0, 15, 0, + rk3588_pll_rates), + [LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3588_LPLL_CON(16), + RK3588_LPLL_MODE_CON, 0, 15, 0, rk3588_pll_rates), + [V0PLL] = PLL(pll_rk3588, PLL_V0PLL, RK3588_PLL_CON(88), + RK3588_MODE_CON0, 4, 15, 0, rk3588_pll_rates), + [AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3588_PLL_CON(96), + RK3588_MODE_CON0, 6, 15, 0, rk3588_pll_rates), + [CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3588_PLL_CON(104), + RK3588_MODE_CON0, 8, 15, 0, rk3588_pll_rates), + [GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3588_PLL_CON(112), + RK3588_MODE_CON0, 2, 15, 0, rk3588_pll_rates), + [NPLL] = PLL(pll_rk3588, PLL_NPLL, RK3588_PLL_CON(120), + RK3588_MODE_CON0, 0, 15, 0, rk3588_pll_rates), + [PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3588_PMU_PLL_CON(128), + RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates), +}; + +#ifndef CONFIG_SPL_BUILD +/* + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} +#endif + +static ulong rk3588_center_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 con, sel, rate; + + switch (clk_id) { + case ACLK_CENTER_ROOT: + con = readl(&cru->clksel_con[165]); + sel = (con & ACLK_CENTER_ROOT_SEL_MASK) >> + ACLK_CENTER_ROOT_SEL_SHIFT; + if (sel == ACLK_CENTER_ROOT_SEL_700M) + rate = 702 * MHz; + else if (sel == ACLK_CENTER_ROOT_SEL_400M) + rate = 396 * MHz; + else if (sel == ACLK_CENTER_ROOT_SEL_200M) + rate = 200 * MHz; + else + rate = OSC_HZ; + break; + case ACLK_CENTER_LOW_ROOT: + con = readl(&cru->clksel_con[165]); + sel = (con & ACLK_CENTER_LOW_ROOT_SEL_MASK) >> + ACLK_CENTER_LOW_ROOT_SEL_SHIFT; + if (sel == ACLK_CENTER_LOW_ROOT_SEL_500M) + rate = 500 * MHz; + else if (sel == ACLK_CENTER_LOW_ROOT_SEL_250M) + rate = 250 * MHz; + else if (sel == ACLK_CENTER_LOW_ROOT_SEL_100M) + rate = 100 * MHz; + else + rate = OSC_HZ; + break; + case HCLK_CENTER_ROOT: + con = readl(&cru->clksel_con[165]); + sel = (con & HCLK_CENTER_ROOT_SEL_MASK) >> + HCLK_CENTER_ROOT_SEL_SHIFT; + if (sel == HCLK_CENTER_ROOT_SEL_400M) + rate = 396 * MHz; + else if (sel == HCLK_CENTER_ROOT_SEL_200M) + rate = 200 * MHz; + else if (sel == HCLK_CENTER_ROOT_SEL_100M) + rate = 100 * MHz; + else + rate = OSC_HZ; + break; + case PCLK_CENTER_ROOT: + con = readl(&cru->clksel_con[165]); + sel = (con & PCLK_CENTER_ROOT_SEL_MASK) >> + PCLK_CENTER_ROOT_SEL_SHIFT; + if (sel == PCLK_CENTER_ROOT_SEL_200M) + rate = 200 * MHz; + else if (sel == PCLK_CENTER_ROOT_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_CENTER_ROOT_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3588_center_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) { + case ACLK_CENTER_ROOT: + if (rate >= 700 * MHz) + src_clk = ACLK_CENTER_ROOT_SEL_700M; + else if (rate >= 396 * MHz) + src_clk = ACLK_CENTER_ROOT_SEL_400M; + else if (rate >= 200 * MHz) + src_clk = ACLK_CENTER_ROOT_SEL_200M; + else + src_clk = ACLK_CENTER_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[165], + ACLK_CENTER_ROOT_SEL_MASK, + src_clk << ACLK_CENTER_ROOT_SEL_SHIFT); + break; + case ACLK_CENTER_LOW_ROOT: + if (rate >= 500 * MHz) + src_clk = ACLK_CENTER_LOW_ROOT_SEL_500M; + else if (rate >= 250 * MHz) + src_clk = ACLK_CENTER_LOW_ROOT_SEL_250M; + else if (rate >= 99 * MHz) + src_clk = ACLK_CENTER_LOW_ROOT_SEL_100M; + else + src_clk = ACLK_CENTER_LOW_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[165], + ACLK_CENTER_LOW_ROOT_SEL_MASK, + src_clk << ACLK_CENTER_LOW_ROOT_SEL_SHIFT); + break; + case HCLK_CENTER_ROOT: + if (rate >= 396 * MHz) + src_clk = HCLK_CENTER_ROOT_SEL_400M; + else if (rate >= 198 * MHz) + src_clk = HCLK_CENTER_ROOT_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = HCLK_CENTER_ROOT_SEL_100M; + else + src_clk = HCLK_CENTER_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[165], + HCLK_CENTER_ROOT_SEL_MASK, + src_clk << HCLK_CENTER_ROOT_SEL_SHIFT); + break; + case PCLK_CENTER_ROOT: + if (rate >= 198 * MHz) + src_clk = PCLK_CENTER_ROOT_SEL_200M; + else if (rate >= 99 * MHz) + src_clk = PCLK_CENTER_ROOT_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = PCLK_CENTER_ROOT_SEL_50M; + else + src_clk = PCLK_CENTER_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[165], + PCLK_CENTER_ROOT_SEL_MASK, + src_clk << PCLK_CENTER_ROOT_SEL_SHIFT); + break; + default: + printf("do not support this center freq\n"); + return -EINVAL; + } + + return rk3588_center_get_clk(priv, clk_id); +} + +static ulong rk3588_top_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 con, sel, div, rate, prate; + + switch (clk_id) { + case ACLK_TOP_ROOT: + con = readl(&cru->clksel_con[8]); + div = (con & ACLK_TOP_ROOT_DIV_MASK) >> + ACLK_TOP_ROOT_DIV_SHIFT; + sel = (con & ACLK_TOP_ROOT_SRC_SEL_MASK) >> + ACLK_TOP_ROOT_SRC_SEL_SHIFT; + if (sel == ACLK_TOP_ROOT_SRC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case ACLK_LOW_TOP_ROOT: + con = readl(&cru->clksel_con[8]); + div = (con & ACLK_LOW_TOP_ROOT_DIV_MASK) >> + ACLK_LOW_TOP_ROOT_DIV_SHIFT; + sel = (con & ACLK_LOW_TOP_ROOT_SRC_SEL_MASK) >> + ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT; + if (sel == ACLK_LOW_TOP_ROOT_SRC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case PCLK_TOP_ROOT: + con = readl(&cru->clksel_con[8]); + sel = (con & PCLK_TOP_ROOT_SEL_MASK) >> PCLK_TOP_ROOT_SEL_SHIFT; + if (sel == PCLK_TOP_ROOT_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_TOP_ROOT_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3588_top_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk, src_clk_div; + + switch (clk_id) { + case ACLK_TOP_ROOT: + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[8], + ACLK_TOP_ROOT_DIV_MASK | + ACLK_TOP_ROOT_SRC_SEL_MASK, + (ACLK_TOP_ROOT_SRC_SEL_GPLL << + ACLK_TOP_ROOT_SRC_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_TOP_ROOT_DIV_SHIFT); + break; + case ACLK_LOW_TOP_ROOT: + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[8], + ACLK_LOW_TOP_ROOT_DIV_MASK | + ACLK_LOW_TOP_ROOT_SRC_SEL_MASK, + (ACLK_LOW_TOP_ROOT_SRC_SEL_GPLL << + ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_LOW_TOP_ROOT_DIV_SHIFT); + break; + case PCLK_TOP_ROOT: + if (rate == 100 * MHz) + src_clk = PCLK_TOP_ROOT_SEL_100M; + else if (rate == 50 * MHz) + src_clk = PCLK_TOP_ROOT_SEL_50M; + else + src_clk = PCLK_TOP_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[8], + PCLK_TOP_ROOT_SEL_MASK, + src_clk << PCLK_TOP_ROOT_SEL_SHIFT); + break; + default: + printf("do not support this top freq\n"); + return -EINVAL; + } + + return rk3588_top_get_clk(priv, clk_id); +} + +static ulong rk3588_i2c_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 sel, con; + ulong rate; + + switch (clk_id) { + case CLK_I2C0: + con = readl(&cru->pmuclksel_con[3]); + sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT; + break; + case CLK_I2C1: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C1_SEL_MASK) >> CLK_I2C1_SEL_SHIFT; + break; + case CLK_I2C2: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C2_SEL_MASK) >> CLK_I2C2_SEL_SHIFT; + break; + case CLK_I2C3: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C3_SEL_MASK) >> CLK_I2C3_SEL_SHIFT; + break; + case CLK_I2C4: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C4_SEL_MASK) >> CLK_I2C4_SEL_SHIFT; + break; + case CLK_I2C5: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C5_SEL_MASK) >> CLK_I2C5_SEL_SHIFT; + break; + case CLK_I2C6: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C6_SEL_MASK) >> CLK_I2C6_SEL_SHIFT; + break; + case CLK_I2C7: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C7_SEL_MASK) >> CLK_I2C7_SEL_SHIFT; + break; + case CLK_I2C8: + con = readl(&cru->clksel_con[38]); + sel = (con & CLK_I2C8_SEL_MASK) >> CLK_I2C8_SEL_SHIFT; + break; + default: + return -ENOENT; + } + if (sel == CLK_I2C_SEL_200M) + rate = 200 * MHz; + else + rate = 100 * MHz; + + return rate; +} + +static ulong rk3588_i2c_set_clk(struct rk3588_clk_priv *priv, ulong clk_id, + ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk; + + if (rate >= 198 * MHz) + src_clk = CLK_I2C_SEL_200M; + else + src_clk = CLK_I2C_SEL_100M; + + switch (clk_id) { + case CLK_I2C0: + rk_clrsetreg(&cru->pmuclksel_con[3], CLK_I2C0_SEL_MASK, + src_clk << CLK_I2C0_SEL_SHIFT); + break; + case CLK_I2C1: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C1_SEL_MASK, + src_clk << CLK_I2C1_SEL_SHIFT); + break; + case CLK_I2C2: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C2_SEL_MASK, + src_clk << CLK_I2C2_SEL_SHIFT); + break; + case CLK_I2C3: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C3_SEL_MASK, + src_clk << CLK_I2C3_SEL_SHIFT); + break; + case CLK_I2C4: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C4_SEL_MASK, + src_clk << CLK_I2C4_SEL_SHIFT); + break; + case CLK_I2C5: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C5_SEL_MASK, + src_clk << CLK_I2C5_SEL_SHIFT); + break; + case CLK_I2C6: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C6_SEL_MASK, + src_clk << CLK_I2C6_SEL_SHIFT); + break; + case CLK_I2C7: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C7_SEL_MASK, + src_clk << CLK_I2C7_SEL_SHIFT); + break; + case CLK_I2C8: + rk_clrsetreg(&cru->clksel_con[38], CLK_I2C8_SEL_MASK, + src_clk << CLK_I2C8_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_i2c_get_clk(priv, clk_id); +} + +static ulong rk3588_spi_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[59]); + + switch (clk_id) { + case CLK_SPI0: + sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT; + break; + case CLK_SPI1: + sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT; + break; + case CLK_SPI2: + sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT; + break; + case CLK_SPI3: + sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT; + break; + case CLK_SPI4: + sel = (con & CLK_SPI4_SEL_MASK) >> CLK_SPI4_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_SPI_SEL_200M: + return 200 * MHz; + case CLK_SPI_SEL_150M: + return 150 * MHz; + case CLK_SPI_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3588_spi_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk; + + if (rate >= 198 * MHz) + src_clk = CLK_SPI_SEL_200M; + else if (rate >= 140 * MHz) + src_clk = CLK_SPI_SEL_150M; + else + src_clk = CLK_SPI_SEL_24M; + + switch (clk_id) { + case CLK_SPI0: + rk_clrsetreg(&cru->clksel_con[59], + CLK_SPI0_SEL_MASK, + src_clk << CLK_SPI0_SEL_SHIFT); + break; + case CLK_SPI1: + rk_clrsetreg(&cru->clksel_con[59], + CLK_SPI1_SEL_MASK, + src_clk << CLK_SPI1_SEL_SHIFT); + break; + case CLK_SPI2: + rk_clrsetreg(&cru->clksel_con[59], + CLK_SPI2_SEL_MASK, + src_clk << CLK_SPI2_SEL_SHIFT); + break; + case CLK_SPI3: + rk_clrsetreg(&cru->clksel_con[59], + CLK_SPI3_SEL_MASK, + src_clk << CLK_SPI3_SEL_SHIFT); + break; + case CLK_SPI4: + rk_clrsetreg(&cru->clksel_con[59], + CLK_SPI4_SEL_MASK, + src_clk << CLK_SPI4_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_spi_get_clk(priv, clk_id); +} + +static ulong rk3588_pwm_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 sel, con; + + switch (clk_id) { + case CLK_PWM1: + con = readl(&cru->clksel_con[59]); + sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT; + break; + case CLK_PWM2: + con = readl(&cru->clksel_con[59]); + sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT; + break; + case CLK_PWM3: + con = readl(&cru->clksel_con[60]); + sel = (con & CLK_PWM3_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + break; + case CLK_PMU1PWM: + con = readl(&cru->pmuclksel_con[2]); + sel = (con & CLK_PMU1PWM_SEL_MASK) >> CLK_PMU1PWM_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_PWM_SEL_100M: + return 100 * MHz; + case CLK_PWM_SEL_50M: + return 50 * MHz; + case CLK_PWM_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3588_pwm_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk; + + if (rate >= 99 * MHz) + src_clk = CLK_PWM_SEL_100M; + else if (rate >= 50 * MHz) + src_clk = CLK_PWM_SEL_50M; + else + src_clk = CLK_PWM_SEL_24M; + + switch (clk_id) { + case CLK_PWM1: + rk_clrsetreg(&cru->clksel_con[59], + CLK_PWM1_SEL_MASK, + src_clk << CLK_PWM1_SEL_SHIFT); + break; + case CLK_PWM2: + rk_clrsetreg(&cru->clksel_con[59], + CLK_PWM2_SEL_MASK, + src_clk << CLK_PWM2_SEL_SHIFT); + break; + case CLK_PWM3: + rk_clrsetreg(&cru->clksel_con[60], + CLK_PWM3_SEL_MASK, + src_clk << CLK_PWM3_SEL_SHIFT); + break; + case CLK_PMU1PWM: + rk_clrsetreg(&cru->pmuclksel_con[2], + CLK_PMU1PWM_SEL_MASK, + src_clk << CLK_PMU1PWM_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_pwm_get_clk(priv, clk_id); +} + +static ulong rk3588_adc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 div, sel, con, prate; + + switch (clk_id) { + case CLK_SARADC: + con = readl(&cru->clksel_con[40]); + div = (con & CLK_SARADC_DIV_MASK) >> CLK_SARADC_DIV_SHIFT; + sel = (con & CLK_SARADC_SEL_MASK) >> + CLK_SARADC_SEL_SHIFT; + if (sel == CLK_SARADC_SEL_24M) + prate = OSC_HZ; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case CLK_TSADC: + con = readl(&cru->clksel_con[41]); + div = (con & CLK_TSADC_DIV_MASK) >> + CLK_TSADC_DIV_SHIFT; + sel = (con & CLK_TSADC_SEL_MASK) >> + CLK_TSADC_SEL_SHIFT; + if (sel == CLK_TSADC_SEL_24M) + prate = OSC_HZ; + else + prate = 100 * MHz; + return DIV_TO_RATE(prate, div); + default: + return -ENOENT; + } +} + +static ulong rk3588_adc_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk_div; + + switch (clk_id) { + case CLK_SARADC: + if (!(OSC_HZ % rate)) { + src_clk_div = DIV_ROUND_UP(OSC_HZ, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[40], + CLK_SARADC_SEL_MASK | + CLK_SARADC_DIV_MASK, + (CLK_SARADC_SEL_24M << + CLK_SARADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_SARADC_DIV_SHIFT); + } else { + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[40], + CLK_SARADC_SEL_MASK | + CLK_SARADC_DIV_MASK, + (CLK_SARADC_SEL_GPLL << + CLK_SARADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_SARADC_DIV_SHIFT); + } + break; + case CLK_TSADC: + if (!(OSC_HZ % rate)) { + src_clk_div = DIV_ROUND_UP(OSC_HZ, rate); + assert(src_clk_div - 1 <= 255); + rk_clrsetreg(&cru->clksel_con[41], + CLK_TSADC_SEL_MASK | + CLK_TSADC_DIV_MASK, + (CLK_TSADC_SEL_24M << + CLK_TSADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_TSADC_DIV_SHIFT); + } else { + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + assert(src_clk_div - 1 <= 7); + rk_clrsetreg(&cru->clksel_con[41], + CLK_TSADC_SEL_MASK | + CLK_TSADC_DIV_MASK, + (CLK_TSADC_SEL_GPLL << + CLK_TSADC_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_TSADC_DIV_SHIFT); + } + break; + default: + return -ENOENT; + } + return rk3588_adc_get_clk(priv, clk_id); +} + +static ulong rk3588_mmc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 sel, con, div, prate; + + switch (clk_id) { + case CCLK_SRC_SDIO: + con = readl(&cru->clksel_con[172]); + div = (con & CCLK_SDIO_SRC_DIV_MASK) >> CCLK_SDIO_SRC_DIV_SHIFT; + sel = (con & CCLK_SDIO_SRC_SEL_MASK) >> + CCLK_SDIO_SRC_SEL_SHIFT; + if (sel == CCLK_SDIO_SRC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == CCLK_SDIO_SRC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case CCLK_EMMC: + con = readl(&cru->clksel_con[77]); + div = (con & CCLK_EMMC_DIV_MASK) >> CCLK_EMMC_DIV_SHIFT; + sel = (con & CCLK_EMMC_SEL_MASK) >> + CCLK_EMMC_SEL_SHIFT; + if (sel == CCLK_EMMC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == CCLK_EMMC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case BCLK_EMMC: + con = readl(&cru->clksel_con[78]); + div = (con & BCLK_EMMC_DIV_MASK) >> BCLK_EMMC_DIV_SHIFT; + sel = (con & BCLK_EMMC_SEL_MASK) >> + BCLK_EMMC_SEL_SHIFT; + if (sel == CCLK_EMMC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + case SCLK_SFC: + con = readl(&cru->clksel_con[78]); + div = (con & SCLK_SFC_DIV_MASK) >> SCLK_SFC_DIV_SHIFT; + sel = (con & SCLK_SFC_SEL_MASK) >> + SCLK_SFC_SEL_SHIFT; + if (sel == SCLK_SFC_SEL_GPLL) + prate = priv->gpll_hz; + else if (sel == SCLK_SFC_SEL_CPLL) + prate = priv->cpll_hz; + else + prate = OSC_HZ; + return DIV_TO_RATE(prate, div); + case DCLK_DECOM: + con = readl(&cru->clksel_con[62]); + div = (con & DCLK_DECOM_DIV_MASK) >> DCLK_DECOM_DIV_SHIFT; + sel = (con & DCLK_DECOM_SEL_MASK) >> + DCLK_DECOM_SEL_SHIFT; + if (sel == DCLK_DECOM_SEL_SPLL) + prate = 702 * MHz; + else + prate = priv->gpll_hz; + return DIV_TO_RATE(prate, div); + default: + return -ENOENT; + } +} + +static ulong rk3588_mmc_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk, div; + + switch (clk_id) { + case CCLK_SRC_SDIO: + case CCLK_EMMC: + case SCLK_SFC: + if (!(OSC_HZ % rate)) { + src_clk = SCLK_SFC_SEL_24M; + div = DIV_ROUND_UP(OSC_HZ, rate); + } else if (!(priv->cpll_hz % rate)) { + src_clk = SCLK_SFC_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = SCLK_SFC_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + break; + case BCLK_EMMC: + if (!(priv->cpll_hz % rate)) { + src_clk = CCLK_EMMC_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = CCLK_EMMC_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + break; + case DCLK_DECOM: + if (!(702 * MHz % rate)) { + src_clk = DCLK_DECOM_SEL_SPLL; + div = DIV_ROUND_UP(702 * MHz, rate); + } else { + src_clk = DCLK_DECOM_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + break; + default: + return -ENOENT; + } + + switch (clk_id) { + case CCLK_SRC_SDIO: + rk_clrsetreg(&cru->clksel_con[172], + CCLK_SDIO_SRC_SEL_MASK | + CCLK_SDIO_SRC_DIV_MASK, + (src_clk << CCLK_SDIO_SRC_SEL_SHIFT) | + (div - 1) << CCLK_SDIO_SRC_DIV_SHIFT); + break; + case CCLK_EMMC: + rk_clrsetreg(&cru->clksel_con[77], + CCLK_EMMC_SEL_MASK | + CCLK_EMMC_DIV_MASK, + (src_clk << CCLK_EMMC_SEL_SHIFT) | + (div - 1) << CCLK_EMMC_DIV_SHIFT); + break; + case BCLK_EMMC: + rk_clrsetreg(&cru->clksel_con[78], + BCLK_EMMC_DIV_MASK | + BCLK_EMMC_SEL_MASK, + (src_clk << BCLK_EMMC_SEL_SHIFT) | + (div - 1) << BCLK_EMMC_DIV_SHIFT); + break; + case SCLK_SFC: + rk_clrsetreg(&cru->clksel_con[78], + SCLK_SFC_DIV_MASK | + SCLK_SFC_SEL_MASK, + (src_clk << SCLK_SFC_SEL_SHIFT) | + (div - 1) << SCLK_SFC_DIV_SHIFT); + break; + case DCLK_DECOM: + rk_clrsetreg(&cru->clksel_con[62], + DCLK_DECOM_DIV_MASK | + DCLK_DECOM_SEL_MASK, + (src_clk << DCLK_DECOM_SEL_SHIFT) | + (div - 1) << DCLK_DECOM_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_mmc_get_clk(priv, clk_id); +} + +#ifndef CONFIG_SPL_BUILD +static ulong rk3588_aux16m_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 div, con, parent; + + parent = priv->gpll_hz; + con = readl(&cru->clksel_con[117]); + + switch (clk_id) { + case CLK_AUX16M_0: + div = (con & CLK_AUX16MHZ_0_DIV_MASK) >> CLK_AUX16MHZ_0_DIV_SHIFT; + return DIV_TO_RATE(parent, div); + case CLK_AUX16M_1: + div = (con & CLK_AUX16MHZ_1_DIV_MASK) >> CLK_AUX16MHZ_1_DIV_SHIFT; + return DIV_TO_RATE(parent, div); + default: + return -ENOENT; + } +} + +static ulong rk3588_aux16m_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + u32 div; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + div = DIV_ROUND_UP(priv->gpll_hz, rate); + + switch (clk_id) { + case CLK_AUX16M_0: + rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_0_DIV_MASK, + (div - 1) << CLK_AUX16MHZ_0_DIV_SHIFT); + break; + case CLK_AUX16M_1: + rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_1_DIV_MASK, + (div - 1) << CLK_AUX16MHZ_1_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_aux16m_get_clk(priv, clk_id); +} + +static ulong rk3588_aclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 div, sel, con, parent; + + switch (clk_id) { + case ACLK_VOP_ROOT: + case ACLK_VOP: + con = readl(&cru->clksel_con[110]); + div = (con & ACLK_VOP_ROOT_DIV_MASK) >> ACLK_VOP_ROOT_DIV_SHIFT; + sel = (con & ACLK_VOP_ROOT_SEL_MASK) >> ACLK_VOP_ROOT_SEL_SHIFT; + if (sel == ACLK_VOP_ROOT_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_AUPLL) + parent = priv->aupll_hz; + else if (sel == ACLK_VOP_ROOT_SEL_NPLL) + parent = priv->npll_hz; + else + parent = 702 * MHz; + return DIV_TO_RATE(parent, div); + case ACLK_VOP_LOW_ROOT: + con = readl(&cru->clksel_con[110]); + sel = (con & ACLK_VOP_LOW_ROOT_SEL_MASK) >> + ACLK_VOP_LOW_ROOT_SEL_SHIFT; + if (sel == ACLK_VOP_LOW_ROOT_SEL_400M) + return 396 * MHz; + else if (sel == ACLK_VOP_LOW_ROOT_SEL_200M) + return 200 * MHz; + else if (sel == ACLK_VOP_LOW_ROOT_SEL_100M) + return 100 * MHz; + else + return OSC_HZ; + case HCLK_VOP_ROOT: + con = readl(&cru->clksel_con[110]); + sel = (con & HCLK_VOP_ROOT_SEL_MASK) >> HCLK_VOP_ROOT_SEL_SHIFT; + if (sel == HCLK_VOP_ROOT_SEL_200M) + return 200 * MHz; + else if (sel == HCLK_VOP_ROOT_SEL_100M) + return 100 * MHz; + else if (sel == HCLK_VOP_ROOT_SEL_50M) + return 50 * MHz; + else + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3588_aclk_vop_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int src_clk, div; + + switch (clk_id) { + case ACLK_VOP_ROOT: + case ACLK_VOP: + if (rate >= 850 * MHz) { + src_clk = ACLK_VOP_ROOT_SEL_NPLL; + div = 1; + } else if (rate >= 750 * MHz) { + src_clk = ACLK_VOP_ROOT_SEL_CPLL; + div = 2; + } else if (rate >= 700 * MHz) { + src_clk = ACLK_VOP_ROOT_SEL_SPLL; + div = 1; + } else if (!(priv->cpll_hz % rate)) { + src_clk = ACLK_VOP_ROOT_SEL_CPLL; + div = DIV_ROUND_UP(priv->cpll_hz, rate); + } else { + src_clk = ACLK_VOP_ROOT_SEL_GPLL; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } + rk_clrsetreg(&cru->clksel_con[110], + ACLK_VOP_ROOT_DIV_MASK | + ACLK_VOP_ROOT_SEL_MASK, + (src_clk << ACLK_VOP_ROOT_SEL_SHIFT) | + (div - 1) << ACLK_VOP_ROOT_DIV_SHIFT); + break; + case ACLK_VOP_LOW_ROOT: + if (rate == 400 * MHz || rate == 396 * MHz) + src_clk = ACLK_VOP_LOW_ROOT_SEL_400M; + else if (rate == 200 * MHz) + src_clk = ACLK_VOP_LOW_ROOT_SEL_200M; + else if (rate == 100 * MHz) + src_clk = ACLK_VOP_LOW_ROOT_SEL_100M; + else + src_clk = ACLK_VOP_LOW_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[110], + ACLK_VOP_LOW_ROOT_SEL_MASK, + src_clk << ACLK_VOP_LOW_ROOT_SEL_SHIFT); + break; + case HCLK_VOP_ROOT: + if (rate == 200 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_200M; + else if (rate == 100 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_100M; + else if (rate == 50 * MHz) + src_clk = HCLK_VOP_ROOT_SEL_50M; + else + src_clk = HCLK_VOP_ROOT_SEL_24M; + rk_clrsetreg(&cru->clksel_con[110], + HCLK_VOP_ROOT_SEL_MASK, + src_clk << HCLK_VOP_ROOT_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_aclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3588_dclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 div, sel, con, parent; + + switch (clk_id) { + case DCLK_VOP0: + case DCLK_VOP0_SRC: + con = readl(&cru->clksel_con[111]); + div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP1: + case DCLK_VOP1_SRC: + con = readl(&cru->clksel_con[111]); + div = (con & DCLK1_VOP_SRC_DIV_MASK) >> DCLK1_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP2: + case DCLK_VOP2_SRC: + con = readl(&cru->clksel_con[112]); + div = (con & DCLK2_VOP_SRC_DIV_MASK) >> DCLK2_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP3: + con = readl(&cru->clksel_con[113]); + div = (con & DCLK3_VOP_SRC_DIV_MASK) >> DCLK3_VOP_SRC_DIV_SHIFT; + sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + if (sel == DCLK_VOP_SRC_SEL_AUPLL) + parent = priv->aupll_hz; + else if (sel == DCLK_VOP_SRC_SEL_V0PLL) + parent = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], + priv->cru, V0PLL); + else if (sel == DCLK_VOP_SRC_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == DCLK_VOP_SRC_SEL_CPLL) + parent = priv->cpll_hz; + else + return -ENOENT; + + return DIV_TO_RATE(parent, div); +} + +#define RK3588_VOP_PLL_LIMIT_FREQ 600000000 + +static ulong rk3588_dclk_vop_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, conid, con, sel, div, best_div = 0, best_sel = 0; + u32 mask, div_shift, sel_shift; + + switch (clk_id) { + case DCLK_VOP0: + case DCLK_VOP0_SRC: + conid = 111; + con = readl(&cru->clksel_con[111]); + sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT; + mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK; + div_shift = DCLK0_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK0_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP1: + case DCLK_VOP1_SRC: + conid = 111; + con = readl(&cru->clksel_con[111]); + sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT; + mask = DCLK1_VOP_SRC_SEL_MASK | DCLK1_VOP_SRC_DIV_MASK; + div_shift = DCLK1_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK1_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP2: + case DCLK_VOP2_SRC: + conid = 112; + con = readl(&cru->clksel_con[112]); + sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT; + mask = DCLK2_VOP_SRC_SEL_MASK | DCLK2_VOP_SRC_DIV_MASK; + div_shift = DCLK2_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK2_VOP_SRC_SEL_SHIFT; + break; + case DCLK_VOP3: + conid = 113; + con = readl(&cru->clksel_con[113]); + sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT; + mask = DCLK3_VOP_SRC_SEL_MASK | DCLK3_VOP_SRC_DIV_MASK; + div_shift = DCLK3_VOP_SRC_DIV_SHIFT; + sel_shift = DCLK3_VOP_SRC_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + if (sel == DCLK_VOP_SRC_SEL_V0PLL) { + div = DIV_ROUND_UP(RK3588_VOP_PLL_LIMIT_FREQ, rate); + rk_clrsetreg(&cru->clksel_con[conid], + mask, + DCLK_VOP_SRC_SEL_V0PLL << sel_shift | + ((div - 1) << div_shift)); + rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], + priv->cru, V0PLL, div * rate); + } else { + for (i = 0; i <= DCLK_VOP_SRC_SEL_AUPLL; i++) { + switch (i) { + case DCLK_VOP_SRC_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case DCLK_VOP_SRC_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + case DCLK_VOP_SRC_SEL_AUPLL: + pll_rate = priv->aupll_hz; + break; + case DCLK_VOP_SRC_SEL_V0PLL: + pll_rate = 0; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, rate); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(rate - now) < abs(rate - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n", + pll_rate, best_rate, best_div, best_sel); + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[conid], + mask, + best_sel << sel_shift | + (best_div - 1) << div_shift); + } else { + printf("do not support this vop freq %lu\n", rate); + return -EINVAL; + } + } + return rk3588_dclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3588_gmac_get_clk(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 con, div; + + switch (clk_id) { + case CLK_GMAC0_PTP_REF: + con = readl(&cru->clksel_con[81]); + div = (con & CLK_GMAC0_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + case CLK_GMAC1_PTP_REF: + con = readl(&cru->clksel_con[81]); + div = (con & CLK_GMAC1_PTP_DIV_MASK) >> CLK_GMAC1_PTP_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + case CLK_GMAC_125M: + con = readl(&cru->clksel_con[83]); + div = (con & CLK_GMAC_125M_DIV_MASK) >> CLK_GMAC_125M_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + case CLK_GMAC_50M: + con = readl(&cru->clksel_con[84]); + div = (con & CLK_GMAC_50M_DIV_MASK) >> CLK_GMAC_50M_DIV_SHIFT; + return DIV_TO_RATE(priv->cpll_hz, div); + default: + return -ENOENT; + } +} + +static ulong rk3588_gmac_set_clk(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + int div; + + div = DIV_ROUND_UP(priv->cpll_hz, rate); + + switch (clk_id) { + case CLK_GMAC0_PTP_REF: + rk_clrsetreg(&cru->clksel_con[81], + CLK_GMAC0_PTP_DIV_MASK | CLK_GMAC0_PTP_SEL_MASK, + CLK_GMAC0_PTP_SEL_CPLL << CLK_GMAC0_PTP_SEL_SHIFT | + (div - 1) << CLK_GMAC0_PTP_DIV_SHIFT); + break; + case CLK_GMAC1_PTP_REF: + rk_clrsetreg(&cru->clksel_con[81], + CLK_GMAC1_PTP_DIV_MASK | CLK_GMAC1_PTP_SEL_MASK, + CLK_GMAC1_PTP_SEL_CPLL << CLK_GMAC1_PTP_SEL_SHIFT | + (div - 1) << CLK_GMAC1_PTP_DIV_SHIFT); + break; + + case CLK_GMAC_125M: + rk_clrsetreg(&cru->clksel_con[83], + CLK_GMAC_125M_DIV_MASK | CLK_GMAC_125M_SEL_MASK, + CLK_GMAC_125M_SEL_CPLL << CLK_GMAC_125M_SEL_SHIFT | + (div - 1) << CLK_GMAC_125M_DIV_SHIFT); + break; + case CLK_GMAC_50M: + rk_clrsetreg(&cru->clksel_con[84], + CLK_GMAC_50M_DIV_MASK | CLK_GMAC_50M_SEL_MASK, + CLK_GMAC_50M_SEL_CPLL << CLK_GMAC_50M_SEL_SHIFT | + (div - 1) << CLK_GMAC_50M_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3588_gmac_get_clk(priv, clk_id); +} + +static ulong rk3588_uart_get_rate(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 reg, con, fracdiv, div, src, p_src, p_rate; + unsigned long m, n; + + switch (clk_id) { + case SCLK_UART1: + reg = 41; + break; + case SCLK_UART2: + reg = 43; + break; + case SCLK_UART3: + reg = 45; + break; + case SCLK_UART4: + reg = 47; + break; + case SCLK_UART5: + reg = 49; + break; + case SCLK_UART6: + reg = 51; + break; + case SCLK_UART7: + reg = 53; + break; + case SCLK_UART8: + reg = 55; + break; + case SCLK_UART9: + reg = 57; + break; + default: + return -ENOENT; + } + con = readl(&cru->clksel_con[reg + 2]); + src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT; + con = readl(&cru->clksel_con[reg]); + div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT; + p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT; + if (p_src == CLK_UART_SRC_SEL_GPLL) + p_rate = priv->gpll_hz; + else + p_rate = priv->cpll_hz; + + if (src == CLK_UART_SEL_SRC) { + return DIV_TO_RATE(p_rate, div); + } else if (src == CLK_UART_SEL_FRAC) { + fracdiv = readl(&cru->clksel_con[reg + 1]); + n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return DIV_TO_RATE(p_rate, div) * n / m; + } else { + return OSC_HZ; + } +} + +static ulong rk3588_uart_set_rate(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + u32 reg, clk_src, uart_src, div; + unsigned long m = 0, n = 0, val; + + if (priv->gpll_hz % rate == 0) { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_SRC; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (priv->cpll_hz % rate == 0) { + clk_src = CLK_UART_SRC_SEL_CPLL; + uart_src = CLK_UART_SEL_SRC; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (rate == OSC_HZ) { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_XIN24M; + div = 2; + } else { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_FRAC; + div = 2; + rational_best_approximation(rate, priv->gpll_hz / div, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + } + + switch (clk_id) { + case SCLK_UART1: + reg = 41; + break; + case SCLK_UART2: + reg = 43; + break; + case SCLK_UART3: + reg = 45; + break; + case SCLK_UART4: + reg = 47; + break; + case SCLK_UART5: + reg = 49; + break; + case SCLK_UART6: + reg = 51; + break; + case SCLK_UART7: + reg = 53; + break; + case SCLK_UART8: + reg = 55; + break; + case SCLK_UART9: + reg = 57; + break; + default: + return -ENOENT; + } + rk_clrsetreg(&cru->clksel_con[reg], + CLK_UART_SRC_SEL_MASK | + CLK_UART_SRC_DIV_MASK, + (clk_src << CLK_UART_SRC_SEL_SHIFT) | + ((div - 1) << CLK_UART_SRC_DIV_SHIFT)); + rk_clrsetreg(&cru->clksel_con[reg + 2], + CLK_UART_SEL_MASK, + (uart_src << CLK_UART_SEL_SHIFT)); + if (m && n) { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[reg + 1]); + } + + return rk3588_uart_get_rate(priv, clk_id); +} + +static ulong rk3588_pciephy_get_rate(struct rk3588_clk_priv *priv, ulong clk_id) +{ + struct rk3588_cru *cru = priv->cru; + u32 con, div, src; + + switch (clk_id) { + case CLK_REF_PIPE_PHY0: + con = readl(&cru->clksel_con[177]); + src = (con & CLK_PCIE_PHY0_REF_SEL_MASK) >> CLK_PCIE_PHY0_REF_SEL_SHIFT; + con = readl(&cru->clksel_con[176]); + div = (con & CLK_PCIE_PHY0_PLL_DIV_MASK) >> CLK_PCIE_PHY0_PLL_DIV_SHIFT; + break; + case CLK_REF_PIPE_PHY1: + con = readl(&cru->clksel_con[177]); + src = (con & CLK_PCIE_PHY1_REF_SEL_MASK) >> CLK_PCIE_PHY1_REF_SEL_SHIFT; + con = readl(&cru->clksel_con[176]); + div = (con & CLK_PCIE_PHY1_PLL_DIV_MASK) >> CLK_PCIE_PHY1_PLL_DIV_SHIFT; + break; + case CLK_REF_PIPE_PHY2: + con = readl(&cru->clksel_con[177]); + src = (con & CLK_PCIE_PHY2_REF_SEL_MASK) >> CLK_PCIE_PHY2_REF_SEL_SHIFT; + div = (con & CLK_PCIE_PHY2_PLL_DIV_MASK) >> CLK_PCIE_PHY2_PLL_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + if (src == CLK_PCIE_PHY_REF_SEL_PPLL) + return DIV_TO_RATE(priv->ppll_hz, div); + else + return OSC_HZ; +} + +static ulong rk3588_pciephy_set_rate(struct rk3588_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3588_cru *cru = priv->cru; + u32 clk_src, div; + + if (rate == OSC_HZ) { + clk_src = CLK_PCIE_PHY_REF_SEL_24M; + div = 1; + } else { + clk_src = CLK_PCIE_PHY_REF_SEL_PPLL; + div = DIV_ROUND_UP(priv->ppll_hz, rate); + } + + switch (clk_id) { + case CLK_REF_PIPE_PHY0: + rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY0_REF_SEL_MASK, + (clk_src << CLK_PCIE_PHY0_REF_SEL_SHIFT)); + rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY0_PLL_DIV_MASK, + ((div - 1) << CLK_PCIE_PHY0_PLL_DIV_SHIFT)); + break; + case CLK_REF_PIPE_PHY1: + rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY1_REF_SEL_MASK, + (clk_src << CLK_PCIE_PHY1_REF_SEL_SHIFT)); + rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY1_PLL_DIV_MASK, + ((div - 1) << CLK_PCIE_PHY1_PLL_DIV_SHIFT)); + break; + case CLK_REF_PIPE_PHY2: + rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY2_REF_SEL_MASK | + CLK_PCIE_PHY2_PLL_DIV_MASK, + (clk_src << CLK_PCIE_PHY2_REF_SEL_SHIFT) | + ((div - 1) << CLK_PCIE_PHY2_PLL_DIV_SHIFT)); + break; + default: + return -ENOENT; + } + + return rk3588_pciephy_get_rate(priv, clk_id); +} +#endif + +static ulong rk3588_clk_get_rate(struct clk *clk) +{ + struct rk3588_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + if (!priv->ppll_hz) { + priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], + priv->cru, PPLL); + } + + switch (clk->id) { + case PLL_LPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[LPLL], priv->cru, + LPLL); + break; + case PLL_B0PLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[B0PLL], priv->cru, + B0PLL); + break; + case PLL_B1PLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[B1PLL], priv->cru, + B1PLL); + break; + case PLL_GPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL], priv->cru, + GPLL); + break; + case PLL_CPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL], priv->cru, + CPLL); + break; + case PLL_NPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[NPLL], priv->cru, + NPLL); + break; + case PLL_V0PLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], priv->cru, + V0PLL); + break; + case PLL_AUPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL], priv->cru, + AUPLL); + break; + case PLL_PPLL: + rate = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], priv->cru, + PPLL); + break; + case ACLK_CENTER_ROOT: + case PCLK_CENTER_ROOT: + case HCLK_CENTER_ROOT: + case ACLK_CENTER_LOW_ROOT: + rate = rk3588_center_get_clk(priv, clk->id); + break; + case ACLK_TOP_ROOT: + case PCLK_TOP_ROOT: + case ACLK_LOW_TOP_ROOT: + rate = rk3588_top_get_clk(priv, clk->id); + break; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + case CLK_I2C8: + rate = rk3588_i2c_get_clk(priv, clk->id); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + case CLK_SPI4: + rate = rk3588_spi_get_clk(priv, clk->id); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + case CLK_PMU1PWM: + rate = rk3588_pwm_get_clk(priv, clk->id); + break; + case CLK_SARADC: + case CLK_TSADC: + rate = rk3588_adc_get_clk(priv, clk->id); + break; + case CCLK_SRC_SDIO: + case CCLK_EMMC: + case BCLK_EMMC: + case SCLK_SFC: + case DCLK_DECOM: + rate = rk3588_mmc_get_clk(priv, clk->id); + break; + case TCLK_WDT0: + rate = OSC_HZ; + break; +#ifndef CONFIG_SPL_BUILD + case CLK_AUX16M_0: + case CLK_AUX16M_1: + rk3588_aux16m_get_clk(priv, clk->id); + break; + case ACLK_VOP_ROOT: + case ACLK_VOP: + case ACLK_VOP_LOW_ROOT: + case HCLK_VOP_ROOT: + rate = rk3588_aclk_vop_get_clk(priv, clk->id); + break; + case DCLK_VOP0: + case DCLK_VOP0_SRC: + case DCLK_VOP1: + case DCLK_VOP1_SRC: + case DCLK_VOP2: + case DCLK_VOP2_SRC: + case DCLK_VOP3: + rate = rk3588_dclk_vop_get_clk(priv, clk->id); + break; + case CLK_GMAC0_PTP_REF: + case CLK_GMAC1_PTP_REF: + case CLK_GMAC_125M: + case CLK_GMAC_50M: + rate = rk3588_gmac_get_clk(priv, clk->id); + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + rate = rk3588_uart_get_rate(priv, clk->id); + break; + case CLK_REF_PIPE_PHY0: + case CLK_REF_PIPE_PHY1: + case CLK_REF_PIPE_PHY2: + rate = rk3588_pciephy_get_rate(priv, clk->id); + break; +#endif + default: + return -ENOENT; + } + + return rate; +}; + +static ulong rk3588_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3588_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + if (!priv->ppll_hz) { + priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], + priv->cru, PPLL); + } + + switch (clk->id) { + case PLL_CPLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru, + CPLL, rate); + priv->cpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL], + priv->cru, CPLL); + break; + case PLL_GPLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru, + GPLL, rate); + priv->gpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL], + priv->cru, GPLL); + break; + case PLL_NPLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[NPLL], priv->cru, + NPLL, rate); + break; + case PLL_V0PLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], priv->cru, + V0PLL, rate); + priv->v0pll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], + priv->cru, V0PLL); + break; + case PLL_AUPLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[AUPLL], priv->cru, + AUPLL, rate); + priv->aupll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL], + priv->cru, AUPLL); + break; + case PLL_PPLL: + ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru, + PPLL, rate); + priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], + priv->cru, PPLL); + break; + case ACLK_CENTER_ROOT: + case PCLK_CENTER_ROOT: + case HCLK_CENTER_ROOT: + case ACLK_CENTER_LOW_ROOT: + ret = rk3588_center_set_clk(priv, clk->id, rate); + break; + case ACLK_TOP_ROOT: + case PCLK_TOP_ROOT: + case ACLK_LOW_TOP_ROOT: + ret = rk3588_top_set_clk(priv, clk->id, rate); + break; + case CLK_I2C0: + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + case CLK_I2C6: + case CLK_I2C7: + case CLK_I2C8: + ret = rk3588_i2c_set_clk(priv, clk->id, rate); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + case CLK_SPI4: + ret = rk3588_spi_set_clk(priv, clk->id, rate); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + case CLK_PMU1PWM: + ret = rk3588_pwm_set_clk(priv, clk->id, rate); + break; + case CLK_SARADC: + case CLK_TSADC: + ret = rk3588_adc_set_clk(priv, clk->id, rate); + break; + case CCLK_SRC_SDIO: + case CCLK_EMMC: + case BCLK_EMMC: + case SCLK_SFC: + case DCLK_DECOM: + ret = rk3588_mmc_set_clk(priv, clk->id, rate); + break; + case TCLK_WDT0: + ret = OSC_HZ; + break; +#ifndef CONFIG_SPL_BUILD + case CLK_AUX16M_0: + case CLK_AUX16M_1: + rk3588_aux16m_set_clk(priv, clk->id, rate); + break; + case ACLK_VOP_ROOT: + case ACLK_VOP: + case ACLK_VOP_LOW_ROOT: + case HCLK_VOP_ROOT: + ret = rk3588_aclk_vop_set_clk(priv, clk->id, rate); + break; + case DCLK_VOP0: + case DCLK_VOP0_SRC: + case DCLK_VOP1: + case DCLK_VOP1_SRC: + case DCLK_VOP2: + case DCLK_VOP2_SRC: + case DCLK_VOP3: + ret = rk3588_dclk_vop_set_clk(priv, clk->id, rate); + break; + case CLK_GMAC0_PTP_REF: + case CLK_GMAC1_PTP_REF: + case CLK_GMAC_125M: + case CLK_GMAC_50M: + ret = rk3588_gmac_set_clk(priv, clk->id, rate); + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + ret = rk3588_uart_set_rate(priv, clk->id, rate); + break; + case CLK_REF_PIPE_PHY0: + case CLK_REF_PIPE_PHY1: + case CLK_REF_PIPE_PHY2: + ret = rk3588_pciephy_set_rate(priv, clk->id, rate); + break; +#endif + default: + return -ENOENT; + } + + return ret; +}; + +#define ROCKCHIP_MMC_DELAY_SEL BIT(10) +#define ROCKCHIP_MMC_DEGREE_MASK 0x3 +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) + +#define PSECS_PER_SEC 1000000000000LL +/* + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. + */ +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 + +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) +static int __maybe_unused rk3588_dclk_vop_set_parent(struct clk *clk, + struct clk *parent) +{ + struct rk3588_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3588_cru *cru = priv->cru; + u32 sel; + const char *clock_dev_name = parent->dev->name; + + if (parent->id == PLL_V0PLL) + sel = 2; + else if (parent->id == PLL_GPLL) + sel = 0; + else if (parent->id == PLL_CPLL) + sel = 1; + else + sel = 3; + + switch (clk->id) { + case DCLK_VOP0_SRC: + rk_clrsetreg(&cru->clksel_con[111], DCLK0_VOP_SRC_SEL_MASK, + sel << DCLK0_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VOP1_SRC: + rk_clrsetreg(&cru->clksel_con[111], DCLK1_VOP_SRC_SEL_MASK, + sel << DCLK1_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VOP2_SRC: + rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SRC_SEL_MASK, + sel << DCLK2_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VOP3: + rk_clrsetreg(&cru->clksel_con[113], DCLK3_VOP_SRC_SEL_MASK, + sel << DCLK3_VOP_SRC_SEL_SHIFT); + break; + case DCLK_VOP0: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else if (!strcmp(clock_dev_name, "hdmiphypll_clk1")) + sel = 2; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[112], DCLK0_VOP_SEL_MASK, + sel << DCLK0_VOP_SEL_SHIFT); + break; + case DCLK_VOP1: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else if (!strcmp(clock_dev_name, "hdmiphypll_clk1")) + sel = 2; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[112], DCLK1_VOP_SEL_MASK, + sel << DCLK1_VOP_SEL_SHIFT); + break; + case DCLK_VOP2: + if (!strcmp(clock_dev_name, "hdmiphypll_clk0")) + sel = 1; + else if (!strcmp(clock_dev_name, "hdmiphypll_clk1")) + sel = 2; + else + sel = 0; + rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SEL_MASK, + sel << DCLK2_VOP_SEL_SHIFT); + break; + default: + return -EINVAL; + } + return 0; +} + +static int rk3588_clk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case DCLK_VOP0_SRC: + case DCLK_VOP1_SRC: + case DCLK_VOP2_SRC: + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + case DCLK_VOP3: + return rk3588_dclk_vop_set_parent(clk, parent); + default: + return -ENOENT; + } + + return 0; +} +#endif + +static struct clk_ops rk3588_clk_ops = { + .get_rate = rk3588_clk_get_rate, + .set_rate = rk3588_clk_set_rate, +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) + .set_parent = rk3588_clk_set_parent, +#endif +}; + +static void rk3588_clk_init(struct rk3588_clk_priv *priv) +{ + int ret, div; + + div = DIV_ROUND_UP(GPLL_HZ, 300 * MHz); + rk_clrsetreg(&priv->cru->clksel_con[38], + ACLK_BUS_ROOT_SEL_MASK | + ACLK_BUS_ROOT_DIV_MASK, + div << ACLK_BUS_ROOT_DIV_SHIFT); + + if (priv->cpll_hz != CPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru, + CPLL, CPLL_HZ); + if (!ret) + priv->cpll_hz = CPLL_HZ; + } + if (priv->gpll_hz != GPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru, + GPLL, GPLL_HZ); + if (!ret) + priv->gpll_hz = GPLL_HZ; + } + +#ifdef CONFIG_PCI + if (priv->ppll_hz != PPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru, + PPLL, PPLL_HZ); + priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], + priv->cru, PPLL); + } +#endif + rk_clrsetreg(&priv->cru->clksel_con[9], + ACLK_TOP_S400_SEL_MASK | + ACLK_TOP_S200_SEL_MASK, + (ACLK_TOP_S400_SEL_400M << ACLK_TOP_S400_SEL_SHIFT) | + (ACLK_TOP_S200_SEL_200M << ACLK_TOP_S200_SEL_SHIFT)); +} + +static int rk3588_clk_probe(struct udevice *dev) +{ + struct rk3588_clk_priv *priv = dev_get_priv(dev); + int ret; + + priv->sync_kernel = false; + +#ifdef CONFIG_SPL_BUILD + rockchip_pll_set_rate(&rk3588_pll_clks[B0PLL], priv->cru, + B0PLL, LPLL_HZ); + rockchip_pll_set_rate(&rk3588_pll_clks[B1PLL], priv->cru, + B1PLL, LPLL_HZ); + if (!priv->armclk_enter_hz) { + ret = rockchip_pll_set_rate(&rk3588_pll_clks[LPLL], priv->cru, + LPLL, LPLL_HZ); + priv->armclk_enter_hz = + rockchip_pll_get_rate(&rk3588_pll_clks[LPLL], + priv->cru, LPLL); + priv->armclk_init_hz = priv->armclk_enter_hz; + } +#endif + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(priv->grf)) + return PTR_ERR(priv->grf); + + rk3588_clk_init(priv); + + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev, 1); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + else + priv->sync_kernel = true; + + return 0; +} + +static int rk3588_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3588_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3588_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child; + struct sysreset_reg *priv; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct rk3588_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct rk3588_cru, + glb_srsr_snd); + dev_set_priv(sys_child, priv); + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rk3588_cru, softrst_con[0]); + ret = rockchip_reset_bind(dev, ret, 49158); + if (ret) + debug("Warning: software reset driver bind faile\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3588_clk_ids[] = { + { .compatible = "rockchip,rk3588-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3588_cru) = { + .name = "rockchip_rk3588_cru", + .id = UCLASS_CLK, + .of_match = rk3588_clk_ids, + .priv_auto = sizeof(struct rk3588_clk_priv), + .of_to_plat = rk3588_clk_ofdata_to_platdata, + .ops = &rk3588_clk_ops, + .bind = rk3588_clk_bind, + .probe = rk3588_clk_probe, +}; -- cgit v1.2.3 From b851c006a15032e535f80c78509491a42f86a1aa Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Jan 2023 20:27:37 +0530 Subject: clk: rockchip: pll: Add pll_rk3588 type for rk3588 Add RK3588 pll set and get rate clock support. Signed-off-by: Elaine Zhang Signed-off-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/clk/rockchip/clk_pll.c | 267 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 264 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c index 09b97cf57a2..d657ef38f3c 100644 --- a/drivers/clk/rockchip/clk_pll.c +++ b/drivers/clk/rockchip/clk_pll.c @@ -45,6 +45,10 @@ enum { #define MIN_FOUTVCO_FREQ (800 * MHZ) #define MAX_FOUTVCO_FREQ (2000 * MHZ) +#define RK3588_VCO_MIN_HZ (2250UL * MHZ) +#define RK3588_VCO_MAX_HZ (4500UL * MHZ) +#define RK3588_FOUT_MIN_HZ (37UL * MHZ) +#define RK3588_FOUT_MAX_HZ (4500UL * MHZ) int gcd(int m, int n) { @@ -164,6 +168,65 @@ rockchip_pll_clk_set_by_auto(ulong fin_hz, return rate_table; } +static struct rockchip_pll_rate_table * +rk3588_pll_clk_set_by_auto(unsigned long fin_hz, + unsigned long fout_hz) +{ + struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; + u32 p, m, s; + ulong fvco, fref, fout, ffrac; + + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) + return NULL; + + if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ) + return NULL; + + if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { + for (s = 0; s <= 6; s++) { + fvco = fout_hz << s; + if (fvco < RK3588_VCO_MIN_HZ || + fvco > RK3588_VCO_MAX_HZ) + continue; + for (p = 2; p <= 4; p++) { + for (m = 64; m <= 1023; m++) { + if (fvco == m * fin_hz / p) { + rate_table->p = p; + rate_table->m = m; + rate_table->s = s; + rate_table->k = 0; + return rate_table; + } + } + } + } + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + } else { + for (s = 0; s <= 6; s++) { + fvco = fout_hz << s; + if (fvco < RK3588_VCO_MIN_HZ || + fvco > RK3588_VCO_MAX_HZ) + continue; + for (p = 1; p <= 4; p++) { + for (m = 64; m <= 1023; m++) { + if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) { + rate_table->p = p; + rate_table->m = m; + rate_table->s = s; + fref = fin_hz / p; + ffrac = fvco - (m * fref); + fout = ffrac * 65536; + rate_table->k = fout / fref; + return rate_table; + } + } + } + } + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + } + return NULL; +} + static const struct rockchip_pll_rate_table * rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) { @@ -174,10 +237,14 @@ rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) break; rate_table++; } - if (rate_table->rate != rate) - return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); - else + if (rate_table->rate != rate) { + if (pll->type == pll_rk3588) + return rk3588_pll_clk_set_by_auto(24 * MHZ, rate); + else + return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); + } else { return rate_table; + } } static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, @@ -296,6 +363,192 @@ static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, } } +#define RK3588_PLLCON(i) ((i) * 0x4) +#define RK3588_PLLCON0_M_MASK 0x3ff << 0 +#define RK3588_PLLCON0_M_SHIFT 0 +#define RK3588_PLLCON1_P_MASK 0x3f << 0 +#define RK3588_PLLCON1_P_SHIFT 0 +#define RK3588_PLLCON1_S_MASK 0x7 << 6 +#define RK3588_PLLCON1_S_SHIFT 6 +#define RK3588_PLLCON2_K_MASK 0xffff +#define RK3588_PLLCON2_K_SHIFT 0 +#define RK3588_PLLCON1_PWRDOWN BIT(13) +#define RK3588_PLLCON6_LOCK_STATUS BIT(15) +#define RK3588_B0PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x50000 + 0x300) +#define RK3588_B1PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x52000 + 0x300) +#define RK3588_LPLL_CLKSEL_CON(i) ((i) * 0x4 + 0x58000 + 0x300) +#define RK3588_CORE_DIV_MASK 0x1f +#define RK3588_CORE_L02_DIV_SHIFT 0 +#define RK3588_CORE_L13_DIV_SHIFT 7 +#define RK3588_CORE_B02_DIV_SHIFT 8 +#define RK3588_CORE_B13_DIV_SHIFT 0 + +static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id, + ulong drate) +{ + const struct rockchip_pll_rate_table *rate; + + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + printf("%s unsupported rate\n", __func__); + return -EINVAL; + } + + debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n", + __func__, rate->rate, rate->p, rate->m, rate->s, rate->k); + + /* + * When power on or changing PLL setting, + * we must force PLL into slow mode to ensure output stable clock. + */ + if (pll_id == 3) + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0x1 << 1); + + rk_clrsetreg(base + pll->mode_offset, + pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_SLOW << pll->mode_shift); + if (pll_id == 0) + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + RKCLK_PLL_MODE_SLOW << 6); + else if (pll_id == 1) + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + RKCLK_PLL_MODE_SLOW << 6); + else if (pll_id == 2) + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), + pll->mode_mask << 14, + RKCLK_PLL_MODE_SLOW << 14); + + /* Power down */ + rk_setreg(base + pll->con_offset + RK3588_PLLCON(1), + RK3588_PLLCON1_PWRDOWN); + + rk_clrsetreg(base + pll->con_offset, + RK3588_PLLCON0_M_MASK, + (rate->m << RK3588_PLLCON0_M_SHIFT)); + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1), + (RK3588_PLLCON1_P_MASK | + RK3588_PLLCON1_S_MASK), + (rate->p << RK3588_PLLCON1_P_SHIFT | + rate->s << RK3588_PLLCON1_S_SHIFT)); + if (rate->k) { + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2), + RK3588_PLLCON2_K_MASK, + rate->k << RK3588_PLLCON2_K_SHIFT); + } + /* Power up */ + rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1), + RK3588_PLLCON1_PWRDOWN); + + /* waiting for pll lock */ + while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) & + RK3588_PLLCON6_LOCK_STATUS)) { + udelay(1); + debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id); + } + + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + if (pll_id == 0) { + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + 2 << 6); + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, + 0 << RK3588_CORE_B02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(1), + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, + 0 << RK3588_CORE_B13_DIV_SHIFT); + } else if (pll_id == 1) { + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + 2 << 6); + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, + 0 << RK3588_CORE_B02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(1), + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, + 0 << RK3588_CORE_B13_DIV_SHIFT); + } else if (pll_id == 2) { + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), + pll->mode_mask << 14, + 2 << 14); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, + 0 << RK3588_CORE_L13_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, + 0 << RK3588_CORE_L02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, + 0 << RK3588_CORE_L13_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, + 0 << RK3588_CORE_L02_DIV_SHIFT); + } + + if (pll_id == 3) + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0); + + debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", + pll, readl(base + pll->con_offset), + readl(base + pll->con_offset + 0x4), + readl(base + pll->con_offset + 0x8), + readl(base + pll->mode_offset)); + + return 0; +} + +static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id) +{ + u32 m, p, s, k; + u32 con = 0, shift, mode; + u64 rate, postdiv; + + con = readl(base + pll->mode_offset); + shift = pll->mode_shift; + if (pll_id == 8) + mode = RKCLK_PLL_MODE_NORMAL; + else + mode = (con & (pll->mode_mask << shift)) >> shift; + switch (mode) { + case RKCLK_PLL_MODE_SLOW: + return OSC_HZ; + case RKCLK_PLL_MODE_NORMAL: + /* normal mode */ + con = readl(base + pll->con_offset); + m = (con & RK3588_PLLCON0_M_MASK) >> + RK3588_PLLCON0_M_SHIFT; + con = readl(base + pll->con_offset + RK3588_PLLCON(1)); + p = (con & RK3588_PLLCON1_P_MASK) >> + RK3036_PLLCON0_FBDIV_SHIFT; + s = (con & RK3588_PLLCON1_S_MASK) >> + RK3588_PLLCON1_S_SHIFT; + con = readl(base + pll->con_offset + RK3588_PLLCON(2)); + k = (con & RK3588_PLLCON2_K_MASK) >> + RK3588_PLLCON2_K_SHIFT; + + rate = OSC_HZ / p; + rate *= m; + if (k) { + /* fractional mode */ + u64 frac_rate64 = OSC_HZ * k; + + postdiv = p * 65536; + do_div(frac_rate64, postdiv); + rate += frac_rate64; + } + rate = rate >> s; + return rate; + case RKCLK_PLL_MODE_DEEP: + default: + return 32768; + } +} + ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, void __iomem *base, ulong pll_id) @@ -311,6 +564,10 @@ ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, pll->mode_mask = PLL_RK3328_MODE_MASK; rate = rk3036_pll_get_rate(pll, base, pll_id); break; + case pll_rk3588: + pll->mode_mask = PLL_MODE_MASK; + rate = rk3588_pll_get_rate(pll, base, pll_id); + break; default: printf("%s: Unknown pll type for pll clk %ld\n", __func__, pll_id); @@ -336,6 +593,10 @@ int rockchip_pll_set_rate(struct rockchip_pll_clock *pll, pll->mode_mask = PLL_RK3328_MODE_MASK; ret = rk3036_pll_set_rate(pll, base, pll_id, drate); break; + case pll_rk3588: + pll->mode_mask = PLL_MODE_MASK; + ret = rk3588_pll_set_rate(pll, base, pll_id, drate); + break; default: printf("%s: Unknown pll type for pll clk %ld\n", __func__, pll_id); -- cgit v1.2.3 From 3b7f29f2c8b6d4f1ad52fb98b22a5f8529a3a1ba Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Jan 2023 20:27:38 +0530 Subject: ram: rockchip: Add rk3588 ddr driver support Add ddr driver for rk3588 to get the ram capacity. Co-developed-by: Jonas Karlman Signed-off-by: Jonas Karlman Signed-off-by: Jagan Teki Reviewed-by: Kever Yang --- drivers/ram/rockchip/Makefile | 1 + drivers/ram/rockchip/sdram_rk3588.c | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 drivers/ram/rockchip/sdram_rk3588.c (limited to 'drivers') diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile index 98839ad6a6c..36dc0500dab 100644 --- a/drivers/ram/rockchip/Makefile +++ b/drivers/ram/rockchip/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_ROCKCHIP_RK3308) = sdram_rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o sdram_pctl_px30.o sdram_phy_px30.o obj-$(CONFIG_ROCKCHIP_RK3399) += sdram_rk3399.o obj-$(CONFIG_ROCKCHIP_RK3568) += sdram_rk3568.o +obj-$(CONFIG_ROCKCHIP_RK3588) += sdram_rk3588.o obj-$(CONFIG_ROCKCHIP_RV1126) += sdram_rv1126.o sdram_pctl_px30.o obj-$(CONFIG_ROCKCHIP_SDRAM_COMMON) += sdram_common.o diff --git a/drivers/ram/rockchip/sdram_rk3588.c b/drivers/ram/rockchip/sdram_rk3588.c new file mode 100644 index 00000000000..cf56e2a9412 --- /dev/null +++ b/drivers/ram/rockchip/sdram_rk3588.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct dram_info { + struct ram_info info; + struct rk3588_pmu1grf *pmugrf; +}; + +static int rk3588_dmc_probe(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + + priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); + priv->info.base = CFG_SYS_SDRAM_BASE; + priv->info.size = + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]) + + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[4]); + + return 0; +} + +static int rk3588_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + struct dram_info *priv = dev_get_priv(dev); + + *info = priv->info; + + return 0; +} + +static struct ram_ops rk3588_dmc_ops = { + .get_info = rk3588_dmc_get_info, +}; + +static const struct udevice_id rk3588_dmc_ids[] = { + { .compatible = "rockchip,rk3588-dmc" }, + { } +}; + +U_BOOT_DRIVER(dmc_rk3588) = { + .name = "rockchip_rk3588_dmc", + .id = UCLASS_RAM, + .of_match = rk3588_dmc_ids, + .ops = &rk3588_dmc_ops, + .probe = rk3588_dmc_probe, + .priv_auto = sizeof(struct dram_info), +}; -- cgit v1.2.3 From 8fa1870e11ebf049af74be94bfaad17006eb18da Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:38 +0000 Subject: rockchip: otp: Refactor to use driver data and ops Refactor the driver to use driver data and ops to simplify handling of SoCs that require a unique read op. Use readl_poll_sleep_timeout instead of a custom poll loop, and add validation of input parameter to main read op. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-otp.c | 85 +++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index cc9a5450e0c..80bf9c1aeac 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -5,10 +5,10 @@ #include #include -#include #include #include #include +#include #include /* OTP Register Offsets */ @@ -49,35 +49,31 @@ struct rockchip_otp_plat { void __iomem *base; - unsigned long secure_conf_base; - unsigned long otp_mask_base; }; -static int rockchip_otp_wait_status(struct rockchip_otp_plat *otp, - u32 flag) +struct rockchip_otp_data { + int (*read)(struct udevice *dev, int offset, void *buf, int size); + int size; +}; + +static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, u32 flag) { - int delay = OTPC_TIMEOUT; - - while (!(readl(otp->base + OTPC_INT_STATUS) & flag)) { - udelay(1); - delay--; - if (delay <= 0) { - printf("%s: wait init status timeout\n", __func__); - return -ETIMEDOUT; - } - } + u32 status; + int ret; + + ret = readl_poll_sleep_timeout(otp->base + OTPC_INT_STATUS, status, + (status & flag), 1, OTPC_TIMEOUT); + if (ret) + return ret; - /* clean int status */ + /* Clear int flag */ writel(flag, otp->base + OTPC_INT_STATUS); return 0; } -static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, - bool enable) +static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable) { - int ret = 0; - writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), otp->base + OTPC_SBPI_CTRL); @@ -92,11 +88,7 @@ static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); - ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); - if (ret < 0) - printf("%s timeout during ecc_enable\n", __func__); - - return ret; + return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE); } static int rockchip_px30_otp_read(struct udevice *dev, int offset, @@ -104,29 +96,26 @@ static int rockchip_px30_otp_read(struct udevice *dev, int offset, { struct rockchip_otp_plat *otp = dev_get_plat(dev); u8 *buffer = buf; - int ret = 0; + int ret; ret = rockchip_otp_ecc_enable(otp, false); - if (ret < 0) { - printf("%s rockchip_otp_ecc_enable err\n", __func__); + if (ret) return ret; - } writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); udelay(5); + while (size--) { writel(offset++ | OTPC_USER_ADDR_MASK, otp->base + OTPC_USER_ADDR); writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, otp->base + OTPC_USER_ENABLE); - ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); - if (ret < 0) { - printf("%s timeout during read setup\n", __func__); + ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE); + if (ret) goto read_end; - } - *buffer++ = readb(otp->base + OTPC_USER_Q); + *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF); } read_end: @@ -138,7 +127,16 @@ read_end: static int rockchip_otp_read(struct udevice *dev, int offset, void *buf, int size) { - return rockchip_px30_otp_read(dev, offset, buf, size); + const struct rockchip_otp_data *data = + (void *)dev_get_driver_data(dev); + + if (offset < 0 || !buf || size <= 0 || offset + size > data->size) + return -EINVAL; + + if (!data->read) + return -ENOSYS; + + return data->read(dev, offset, buf, size); } static const struct misc_ops rockchip_otp_ops = { @@ -147,21 +145,26 @@ static const struct misc_ops rockchip_otp_ops = { static int rockchip_otp_of_to_plat(struct udevice *dev) { - struct rockchip_otp_plat *otp = dev_get_plat(dev); + struct rockchip_otp_plat *plat = dev_get_plat(dev); - otp->base = dev_read_addr_ptr(dev); + plat->base = dev_read_addr_ptr(dev); return 0; } +static const struct rockchip_otp_data px30_data = { + .read = rockchip_px30_otp_read, + .size = 0x40, +}; + static const struct udevice_id rockchip_otp_ids[] = { { .compatible = "rockchip,px30-otp", - .data = (ulong)&rockchip_px30_otp_read, + .data = (ulong)&px30_data, }, { .compatible = "rockchip,rk3308-otp", - .data = (ulong)&rockchip_px30_otp_read, + .data = (ulong)&px30_data, }, {} }; @@ -170,7 +173,7 @@ U_BOOT_DRIVER(rockchip_otp) = { .name = "rockchip_otp", .id = UCLASS_MISC, .of_match = rockchip_otp_ids, - .ops = &rockchip_otp_ops, .of_to_plat = rockchip_otp_of_to_plat, - .plat_auto = sizeof(struct rockchip_otp_plat), + .plat_auto = sizeof(struct rockchip_otp_plat), + .ops = &rockchip_otp_ops, }; -- cgit v1.2.3 From d58d55d242b88711d5ee3561345a8edd0a34e63c Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:39 +0000 Subject: rockchip: otp: Add support for RK3568 Add support for rk3568 compatible. Handle allocation of an aligned bounce buffer in main read op in order to keep the SoC unique read op simple. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-otp.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index 80bf9c1aeac..fea3ca7c288 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -9,6 +9,7 @@ #include #include #include +#include #include /* OTP Register Offsets */ @@ -54,6 +55,7 @@ struct rockchip_otp_plat { struct rockchip_otp_data { int (*read)(struct udevice *dev, int offset, void *buf, int size); int size; + int block_size; }; static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, u32 flag) @@ -124,11 +126,47 @@ read_end: return ret; } +static int rockchip_rk3568_otp_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_otp_plat *otp = dev_get_plat(dev); + u16 *buffer = buf; + int ret; + + ret = rockchip_otp_ecc_enable(otp, false); + if (ret) + return ret; + + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + + while (size--) { + writel(offset++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + + ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE); + if (ret) + goto read_end; + + *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF); + } + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + + return ret; +} + static int rockchip_otp_read(struct udevice *dev, int offset, void *buf, int size) { const struct rockchip_otp_data *data = (void *)dev_get_driver_data(dev); + u32 block_start, block_end, block_offset, blocks; + u8 *buffer; + int ret; if (offset < 0 || !buf || size <= 0 || offset + size > data->size) return -EINVAL; @@ -136,7 +174,24 @@ static int rockchip_otp_read(struct udevice *dev, int offset, if (!data->read) return -ENOSYS; - return data->read(dev, offset, buf, size); + if (data->block_size <= 1) + return data->read(dev, offset, buf, size); + + block_start = offset / data->block_size; + block_offset = offset % data->block_size; + block_end = DIV_ROUND_UP(offset + size, data->block_size); + blocks = block_end - block_start; + + buffer = calloc(blocks, data->block_size); + if (!buffer) + return -ENOMEM; + + ret = data->read(dev, block_start, buffer, blocks); + if (!ret) + memcpy(buf, buffer + block_offset, size); + + free(buffer); + return ret; } static const struct misc_ops rockchip_otp_ops = { @@ -157,6 +212,12 @@ static const struct rockchip_otp_data px30_data = { .size = 0x40, }; +static const struct rockchip_otp_data rk3568_data = { + .read = rockchip_rk3568_otp_read, + .size = 0x80, + .block_size = 2, +}; + static const struct udevice_id rockchip_otp_ids[] = { { .compatible = "rockchip,px30-otp", @@ -166,6 +227,10 @@ static const struct udevice_id rockchip_otp_ids[] = { .compatible = "rockchip,rk3308-otp", .data = (ulong)&px30_data, }, + { + .compatible = "rockchip,rk3568-otp", + .data = (ulong)&rk3568_data, + }, {} }; -- cgit v1.2.3 From f888098229174514029c02d76918e27be50d9e1a Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:39 +0000 Subject: rockchip: otp: Add support for RK3588 Add support for rk3588 compatible. Adjust offset using driver data in main read op. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-otp.c | 63 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index fea3ca7c288..3da99e83179 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -48,28 +48,41 @@ #define OTPC_TIMEOUT 10000 +#define RK3588_OTPC_AUTO_CTRL 0x0004 +#define RK3588_ADDR_SHIFT 16 +#define RK3588_ADDR(n) ((n) << RK3588_ADDR_SHIFT) +#define RK3588_BURST_SHIFT 8 +#define RK3588_BURST(n) ((n) << RK3588_BURST_SHIFT) +#define RK3588_OTPC_AUTO_EN 0x0008 +#define RK3588_AUTO_EN BIT(0) +#define RK3588_OTPC_DOUT0 0x0020 +#define RK3588_OTPC_INT_ST 0x0084 +#define RK3588_RD_DONE BIT(1) + struct rockchip_otp_plat { void __iomem *base; }; struct rockchip_otp_data { int (*read)(struct udevice *dev, int offset, void *buf, int size); + int offset; int size; int block_size; }; -static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, u32 flag) +static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, + u32 flag, u32 reg) { u32 status; int ret; - ret = readl_poll_sleep_timeout(otp->base + OTPC_INT_STATUS, status, + ret = readl_poll_sleep_timeout(otp->base + reg, status, (status & flag), 1, OTPC_TIMEOUT); if (ret) return ret; /* Clear int flag */ - writel(flag, otp->base + OTPC_INT_STATUS); + writel(flag, otp->base + reg); return 0; } @@ -90,7 +103,7 @@ static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable) writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); - return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE); + return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE, OTPC_INT_STATUS); } static int rockchip_px30_otp_read(struct udevice *dev, int offset, @@ -113,7 +126,8 @@ static int rockchip_px30_otp_read(struct udevice *dev, int offset, writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, otp->base + OTPC_USER_ENABLE); - ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE); + ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE, + OTPC_INT_STATUS); if (ret) goto read_end; @@ -146,7 +160,8 @@ static int rockchip_rk3568_otp_read(struct udevice *dev, int offset, writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, otp->base + OTPC_USER_ENABLE); - ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE); + ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE, + OTPC_INT_STATUS); if (ret) goto read_end; @@ -159,6 +174,29 @@ read_end: return ret; } +static int rockchip_rk3588_otp_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_otp_plat *otp = dev_get_plat(dev); + u32 *buffer = buf; + int ret; + + while (size--) { + writel(RK3588_ADDR(offset++) | RK3588_BURST(1), + otp->base + RK3588_OTPC_AUTO_CTRL); + writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN); + + ret = rockchip_otp_poll_timeout(otp, RK3588_RD_DONE, + RK3588_OTPC_INT_ST); + if (ret) + return ret; + + *buffer++ = readl(otp->base + RK3588_OTPC_DOUT0); + } + + return 0; +} + static int rockchip_otp_read(struct udevice *dev, int offset, void *buf, int size) { @@ -174,6 +212,8 @@ static int rockchip_otp_read(struct udevice *dev, int offset, if (!data->read) return -ENOSYS; + offset += data->offset; + if (data->block_size <= 1) return data->read(dev, offset, buf, size); @@ -218,6 +258,13 @@ static const struct rockchip_otp_data rk3568_data = { .block_size = 2, }; +static const struct rockchip_otp_data rk3588_data = { + .read = rockchip_rk3588_otp_read, + .offset = 0xC00, + .size = 0x400, + .block_size = 4, +}; + static const struct udevice_id rockchip_otp_ids[] = { { .compatible = "rockchip,px30-otp", @@ -231,6 +278,10 @@ static const struct udevice_id rockchip_otp_ids[] = { .compatible = "rockchip,rk3568-otp", .data = (ulong)&rk3568_data, }, + { + .compatible = "rockchip,rk3588-otp", + .data = (ulong)&rk3588_data, + }, {} }; -- cgit v1.2.3 From 9a850d1fcd2bd2def1adceef58b48d9a9cf3e309 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:39 +0000 Subject: rockchip: otp: Add dump_otp debug command Add a simple debug command to dump the content of the otp. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-otp.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c index 3da99e83179..c19cd5ce625 100644 --- a/drivers/misc/rockchip-otp.c +++ b/drivers/misc/rockchip-otp.c @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include #include @@ -70,6 +72,39 @@ struct rockchip_otp_data { int block_size; }; +#if defined(DEBUG) +static int dump_otp(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + u8 data[4]; + int ret, i; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_DRIVER_GET(rockchip_otp), &dev); + if (ret) { + printf("%s: no misc-device found\n", __func__); + return 0; + } + + for (i = 0; true; i += sizeof(data)) { + ret = misc_read(dev, i, &data, sizeof(data)); + if (ret < 0) + return 0; + + print_buffer(i, data, 1, sizeof(data), sizeof(data)); + } + + return 0; +} + +U_BOOT_CMD( + dump_otp, 1, 1, dump_otp, + "Dump the content of the otp", + "" +); +#endif + static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, u32 flag, u32 reg) { -- cgit v1.2.3 From 433260ac1a69b5f3467e97328978a707fb2d6e3a Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:39 +0000 Subject: rockchip: efuse: Refactor to use driver data and ops Refactor the driver to use driver data and ops to simplify handling of SoCs that require a unique read op. Move handling of the aligned bounce buffer to main read op in order to keep the SoC unique read op simple. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-efuse.c | 160 ++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c index 083ee65e0ad..7d4a984dce3 100644 --- a/drivers/misc/rockchip-efuse.c +++ b/drivers/misc/rockchip-efuse.c @@ -13,50 +13,40 @@ #include #include #include +#include #include -#define RK3399_A_SHIFT 16 -#define RK3399_A_MASK 0x3ff -#define RK3399_NFUSES 32 -#define RK3399_BYTES_PER_FUSE 4 -#define RK3399_STROBSFTSEL BIT(9) -#define RK3399_RSB BIT(7) -#define RK3399_PD BIT(5) -#define RK3399_PGENB BIT(3) -#define RK3399_LOAD BIT(2) -#define RK3399_STROBE BIT(1) -#define RK3399_CSB BIT(0) - -struct rockchip_efuse_regs { - u32 ctrl; /* 0x00 efuse control register */ - u32 dout; /* 0x04 efuse data out register */ - u32 rf; /* 0x08 efuse redundancy bit used register */ - u32 _rsvd0; - u32 jtag_pass; /* 0x10 JTAG password */ - u32 strobe_finish_ctrl; - /* 0x14 efuse strobe finish control register */ -}; +#define EFUSE_CTRL 0x0000 +#define RK3399_A_SHIFT 16 +#define RK3399_A_MASK GENMASK(25, 16) +#define RK3399_ADDR(n) ((n) << RK3399_A_SHIFT) +#define RK3399_STROBSFTSEL BIT(9) +#define RK3399_RSB BIT(7) +#define RK3399_PD BIT(5) +#define RK3399_PGENB BIT(3) +#define RK3399_LOAD BIT(2) +#define RK3399_STROBE BIT(1) +#define RK3399_CSB BIT(0) +#define EFUSE_DOUT 0x0004 struct rockchip_efuse_plat { void __iomem *base; - struct clk *clk; +}; + +struct rockchip_efuse_data { + int (*read)(struct udevice *dev, int offset, void *buf, int size); + int size; + int block_size; }; #if defined(DEBUG) -static int dump_efuses(struct cmd_tbl *cmdtp, int flag, - int argc, char *const argv[]) +static int dump_efuse(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) { - /* - * N.B.: This function is tailored towards the RK3399 and assumes that - * there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to - * be read. - */ - struct udevice *dev; - u8 fuses[128]; - int ret; + u8 data[4]; + int ret, i; - /* retrieve the device */ ret = uclass_get_device_by_driver(UCLASS_MISC, DM_DRIVER_GET(rockchip_efuse), &dev); if (ret) { @@ -64,21 +54,20 @@ static int dump_efuses(struct cmd_tbl *cmdtp, int flag, return 0; } - ret = misc_read(dev, 0, &fuses, sizeof(fuses)); - if (ret < 0) { - printf("%s: misc_read failed\n", __func__); - return 0; - } + for (i = 0; true; i += sizeof(data)) { + ret = misc_read(dev, i, &data, sizeof(data)); + if (ret < 0) + return 0; - printf("efuse-contents:\n"); - print_buffer(0, fuses, 1, 128, 16); + print_buffer(i, data, 1, sizeof(data), sizeof(data)); + } return 0; } U_BOOT_CMD( - rk3399_dump_efuses, 1, 1, dump_efuses, - "Dump the content of the efuses", + dump_efuse, 1, 1, dump_efuse, + "Dump the content of the efuse", "" ); #endif @@ -86,43 +75,25 @@ U_BOOT_CMD( static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, void *buf, int size) { - struct rockchip_efuse_plat *plat = dev_get_plat(dev); - struct rockchip_efuse_regs *efuse = - (struct rockchip_efuse_regs *)plat->base; - - unsigned int addr_start, addr_end, addr_offset; - u32 out_value; - u8 bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE]; - int i = 0; - u32 addr; - - addr_start = offset / RK3399_BYTES_PER_FUSE; - addr_offset = offset % RK3399_BYTES_PER_FUSE; - addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE); - - /* cap to the size of the efuse block */ - if (addr_end > RK3399_NFUSES) - addr_end = RK3399_NFUSES; + struct rockchip_efuse_plat *efuse = dev_get_plat(dev); + u32 *buffer = buf; + /* Switch to array read mode */ writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, - &efuse->ctrl); + efuse->base + EFUSE_CTRL); udelay(1); - for (addr = addr_start; addr < addr_end; addr++) { - setbits_le32(&efuse->ctrl, - RK3399_STROBE | (addr << RK3399_A_SHIFT)); + + while (size--) { + setbits_le32(efuse->base + EFUSE_CTRL, + RK3399_STROBE | RK3399_ADDR(offset++)); udelay(1); - out_value = readl(&efuse->dout); - clrbits_le32(&efuse->ctrl, RK3399_STROBE); + *buffer++ = readl(efuse->base + EFUSE_DOUT); + clrbits_le32(efuse->base + EFUSE_CTRL, RK3399_STROBE); udelay(1); - - memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE); - i += RK3399_BYTES_PER_FUSE; } - /* Switch to standby mode */ - writel(RK3399_PD | RK3399_CSB, &efuse->ctrl); - - memcpy(buf, bytes + addr_offset, size); + /* Switch to power-down mode */ + writel(RK3399_PD | RK3399_CSB, efuse->base + EFUSE_CTRL); return 0; } @@ -130,7 +101,36 @@ static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, static int rockchip_efuse_read(struct udevice *dev, int offset, void *buf, int size) { - return rockchip_rk3399_efuse_read(dev, offset, buf, size); + const struct rockchip_efuse_data *data = + (void *)dev_get_driver_data(dev); + u32 block_start, block_end, block_offset, blocks; + u8 *buffer; + int ret; + + if (offset < 0 || !buf || size <= 0 || offset + size > data->size) + return -EINVAL; + + if (!data->read) + return -ENOSYS; + + if (data->block_size <= 1) + return data->read(dev, offset, buf, size); + + block_start = offset / data->block_size; + block_offset = offset % data->block_size; + block_end = DIV_ROUND_UP(offset + size, data->block_size); + blocks = block_end - block_start; + + buffer = calloc(blocks, data->block_size); + if (!buffer) + return -ENOMEM; + + ret = data->read(dev, block_start, buffer, blocks); + if (!ret) + memcpy(buf, buffer + block_offset, size); + + free(buffer); + return ret; } static const struct misc_ops rockchip_efuse_ops = { @@ -142,11 +142,21 @@ static int rockchip_efuse_of_to_plat(struct udevice *dev) struct rockchip_efuse_plat *plat = dev_get_plat(dev); plat->base = dev_read_addr_ptr(dev); + return 0; } +static const struct rockchip_efuse_data rk3399_data = { + .read = rockchip_rk3399_efuse_read, + .size = 0x80, + .block_size = 4, +}; + static const struct udevice_id rockchip_efuse_ids[] = { - { .compatible = "rockchip,rk3399-efuse" }, + { + .compatible = "rockchip,rk3399-efuse", + .data = (ulong)&rk3399_data, + }, {} }; @@ -155,6 +165,6 @@ U_BOOT_DRIVER(rockchip_efuse) = { .id = UCLASS_MISC, .of_match = rockchip_efuse_ids, .of_to_plat = rockchip_efuse_of_to_plat, - .plat_auto = sizeof(struct rockchip_efuse_plat), + .plat_auto = sizeof(struct rockchip_efuse_plat), .ops = &rockchip_efuse_ops, }; -- cgit v1.2.3 From 6f95b3e1034fb491a04479d792d35130ba41d887 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:40 +0000 Subject: rockchip: efuse: Add support for RK3288 and more Add support for rk3066a, rk3188, rk322x and rk3288 compatible. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/Kconfig | 4 --- drivers/misc/rockchip-efuse.c | 68 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b07261d3db5..b5707a15c50 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -92,10 +92,6 @@ config ROCKCHIP_EFUSE or through child-nodes that are generated based on the e-fuse map retrieved from the DTS. - This driver currently supports the RK3399 only, but can easily be - extended (by porting the read function from the Linux kernel sources) - to support other recent Rockchip devices. - config ROCKCHIP_OTP bool "Rockchip OTP Support" depends on MISC diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c index 7d4a984dce3..99220d5b1ea 100644 --- a/drivers/misc/rockchip-efuse.c +++ b/drivers/misc/rockchip-efuse.c @@ -17,16 +17,19 @@ #include #define EFUSE_CTRL 0x0000 +#define RK3288_A_SHIFT 6 +#define RK3288_A_MASK GENMASK(15, 6) +#define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT) #define RK3399_A_SHIFT 16 #define RK3399_A_MASK GENMASK(25, 16) #define RK3399_ADDR(n) ((n) << RK3399_A_SHIFT) #define RK3399_STROBSFTSEL BIT(9) #define RK3399_RSB BIT(7) #define RK3399_PD BIT(5) -#define RK3399_PGENB BIT(3) -#define RK3399_LOAD BIT(2) -#define RK3399_STROBE BIT(1) -#define RK3399_CSB BIT(0) +#define EFUSE_PGENB BIT(3) +#define EFUSE_LOAD BIT(2) +#define EFUSE_STROBE BIT(1) +#define EFUSE_CSB BIT(0) #define EFUSE_DOUT 0x0004 struct rockchip_efuse_plat { @@ -72,6 +75,34 @@ U_BOOT_CMD( ); #endif +static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_efuse_plat *efuse = dev_get_plat(dev); + u8 *buffer = buf; + + /* Switch to read mode */ + writel(EFUSE_CSB, efuse->base + EFUSE_CTRL); + writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + EFUSE_CTRL); + udelay(2); + + while (size--) { + clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3288_A_MASK, + RK3288_ADDR(offset++)); + udelay(2); + setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); + clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + } + + /* Switch to standby mode */ + writel(EFUSE_CSB | EFUSE_PGENB, efuse->base + EFUSE_CTRL); + + return 0; +} + static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, void *buf, int size) { @@ -79,21 +110,21 @@ static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, u32 *buffer = buf; /* Switch to array read mode */ - writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, + writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, efuse->base + EFUSE_CTRL); udelay(1); while (size--) { setbits_le32(efuse->base + EFUSE_CTRL, - RK3399_STROBE | RK3399_ADDR(offset++)); + EFUSE_STROBE | RK3399_ADDR(offset++)); udelay(1); *buffer++ = readl(efuse->base + EFUSE_DOUT); - clrbits_le32(efuse->base + EFUSE_CTRL, RK3399_STROBE); + clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); udelay(1); } /* Switch to power-down mode */ - writel(RK3399_PD | RK3399_CSB, efuse->base + EFUSE_CTRL); + writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL); return 0; } @@ -146,6 +177,11 @@ static int rockchip_efuse_of_to_plat(struct udevice *dev) return 0; } +static const struct rockchip_efuse_data rk3288_data = { + .read = rockchip_rk3288_efuse_read, + .size = 0x20, +}; + static const struct rockchip_efuse_data rk3399_data = { .read = rockchip_rk3399_efuse_read, .size = 0x80, @@ -153,6 +189,22 @@ static const struct rockchip_efuse_data rk3399_data = { }; static const struct udevice_id rockchip_efuse_ids[] = { + { + .compatible = "rockchip,rk3066a-efuse", + .data = (ulong)&rk3288_data, + }, + { + .compatible = "rockchip,rk3188-efuse", + .data = (ulong)&rk3288_data, + }, + { + .compatible = "rockchip,rk3228-efuse", + .data = (ulong)&rk3288_data, + }, + { + .compatible = "rockchip,rk3288-efuse", + .data = (ulong)&rk3288_data, + }, { .compatible = "rockchip,rk3399-efuse", .data = (ulong)&rk3399_data, -- cgit v1.2.3 From b0828cf98901c993f7a88ef8f5b7f97a1c0c5d51 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:40 +0000 Subject: rockchip: efuse: Add support for RK3328 Add support for rk3328 compatible. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-efuse.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c index 99220d5b1ea..8cf61e36aef 100644 --- a/drivers/misc/rockchip-efuse.c +++ b/drivers/misc/rockchip-efuse.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,12 @@ #define EFUSE_STROBE BIT(1) #define EFUSE_CSB BIT(0) #define EFUSE_DOUT 0x0004 +#define RK3328_INT_STATUS 0x0018 +#define RK3328_INT_FINISH BIT(0) +#define RK3328_DOUT 0x0020 +#define RK3328_AUTO_CTRL 0x0024 +#define RK3328_AUTO_RD BIT(1) +#define RK3328_AUTO_ENB BIT(0) struct rockchip_efuse_plat { void __iomem *base; @@ -38,6 +45,7 @@ struct rockchip_efuse_plat { struct rockchip_efuse_data { int (*read)(struct udevice *dev, int offset, void *buf, int size); + int offset; int size; int block_size; }; @@ -103,6 +111,30 @@ static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, return 0; } +static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_efuse_plat *efuse = dev_get_plat(dev); + u32 status, *buffer = buf; + int ret; + + while (size--) { + writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | RK3399_ADDR(offset++), + efuse->base + RK3328_AUTO_CTRL); + udelay(1); + + ret = readl_poll_sleep_timeout(efuse->base + RK3328_INT_STATUS, + status, (status & RK3328_INT_FINISH), 1, 50); + if (ret) + return ret; + + *buffer++ = readl(efuse->base + RK3328_DOUT); + writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); + } + + return 0; +} + static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, void *buf, int size) { @@ -144,6 +176,8 @@ static int rockchip_efuse_read(struct udevice *dev, int offset, if (!data->read) return -ENOSYS; + offset += data->offset; + if (data->block_size <= 1) return data->read(dev, offset, buf, size); @@ -182,6 +216,13 @@ static const struct rockchip_efuse_data rk3288_data = { .size = 0x20, }; +static const struct rockchip_efuse_data rk3328_data = { + .read = rockchip_rk3328_efuse_read, + .offset = 0x60, + .size = 0x20, + .block_size = 4, +}; + static const struct rockchip_efuse_data rk3399_data = { .read = rockchip_rk3399_efuse_read, .size = 0x80, @@ -205,6 +246,10 @@ static const struct udevice_id rockchip_efuse_ids[] = { .compatible = "rockchip,rk3288-efuse", .data = (ulong)&rk3288_data, }, + { + .compatible = "rockchip,rk3328-efuse", + .data = (ulong)&rk3328_data, + }, { .compatible = "rockchip,rk3399-efuse", .data = (ulong)&rk3399_data, -- cgit v1.2.3 From 16e8afa802ebc3869c99d57186720476f3affd6b Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:40 +0000 Subject: rockchip: efuse: Add support for RK3128 Add support for rk3128 compatible. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-efuse.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c index 8cf61e36aef..bc73fd3baa1 100644 --- a/drivers/misc/rockchip-efuse.c +++ b/drivers/misc/rockchip-efuse.c @@ -18,6 +18,9 @@ #include #define EFUSE_CTRL 0x0000 +#define RK3128_A_SHIFT 7 +#define RK3128_A_MASK GENMASK(15, 7) +#define RK3128_ADDR(n) ((n) << RK3128_A_SHIFT) #define RK3288_A_SHIFT 6 #define RK3288_A_MASK GENMASK(15, 6) #define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT) @@ -83,6 +86,33 @@ U_BOOT_CMD( ); #endif +static int rockchip_rk3128_efuse_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_efuse_plat *efuse = dev_get_plat(dev); + u8 *buffer = buf; + + /* Switch to read mode */ + writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL); + udelay(2); + + while (size--) { + clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3128_A_MASK, + RK3128_ADDR(offset++)); + udelay(2); + setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); + clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + } + + /* Switch to inactive mode */ + writel(0x0, efuse->base + EFUSE_CTRL); + + return 0; +} + static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, void *buf, int size) { @@ -211,6 +241,11 @@ static int rockchip_efuse_of_to_plat(struct udevice *dev) return 0; } +static const struct rockchip_efuse_data rk3128_data = { + .read = rockchip_rk3128_efuse_read, + .size = 0x40, +}; + static const struct rockchip_efuse_data rk3288_data = { .read = rockchip_rk3288_efuse_read, .size = 0x20, @@ -234,6 +269,10 @@ static const struct udevice_id rockchip_efuse_ids[] = { .compatible = "rockchip,rk3066a-efuse", .data = (ulong)&rk3288_data, }, + { + .compatible = "rockchip,rk3128-efuse", + .data = (ulong)&rk3128_data, + }, { .compatible = "rockchip,rk3188-efuse", .data = (ulong)&rk3288_data, -- cgit v1.2.3 From dd5684b87850ad535ec4fd6532cf675d07b73983 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 22 Feb 2023 22:44:40 +0000 Subject: rockchip: efuse: Add support for RK3036 Add support for rk3036 compatible. Signed-off-by: Jonas Karlman Reviewed-by: Kever Yang --- drivers/misc/rockchip-efuse.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c index bc73fd3baa1..60931a51312 100644 --- a/drivers/misc/rockchip-efuse.c +++ b/drivers/misc/rockchip-efuse.c @@ -18,6 +18,9 @@ #include #define EFUSE_CTRL 0x0000 +#define RK3036_A_SHIFT 8 +#define RK3036_A_MASK GENMASK(15, 8) +#define RK3036_ADDR(n) ((n) << RK3036_A_SHIFT) #define RK3128_A_SHIFT 7 #define RK3128_A_MASK GENMASK(15, 7) #define RK3128_ADDR(n) ((n) << RK3128_A_SHIFT) @@ -86,6 +89,33 @@ U_BOOT_CMD( ); #endif +static int rockchip_rk3036_efuse_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct rockchip_efuse_plat *efuse = dev_get_plat(dev); + u8 *buffer = buf; + + /* Switch to read mode */ + writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL); + udelay(2); + + while (size--) { + clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3036_A_MASK, + RK3036_ADDR(offset++)); + udelay(2); + setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); + clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); + udelay(2); + } + + /* Switch to inactive mode */ + writel(0x0, efuse->base + EFUSE_CTRL); + + return 0; +} + static int rockchip_rk3128_efuse_read(struct udevice *dev, int offset, void *buf, int size) { @@ -241,6 +271,11 @@ static int rockchip_efuse_of_to_plat(struct udevice *dev) return 0; } +static const struct rockchip_efuse_data rk3036_data = { + .read = rockchip_rk3036_efuse_read, + .size = 0x20, +}; + static const struct rockchip_efuse_data rk3128_data = { .read = rockchip_rk3128_efuse_read, .size = 0x40, @@ -265,6 +300,10 @@ static const struct rockchip_efuse_data rk3399_data = { }; static const struct udevice_id rockchip_efuse_ids[] = { + { + .compatible = "rockchip,rk3036-efuse", + .data = (ulong)&rk3036_data, + }, { .compatible = "rockchip,rk3066a-efuse", .data = (ulong)&rk3288_data, -- cgit v1.2.3 From f0eb365e21d93d7d8b34cd84a8db2f26cab6ee9a Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Thu, 23 Feb 2023 13:03:32 -0800 Subject: clk: rockchip: rk3568: add more supported clk rates for sdmmc and emmc SDHCI driver may attempt to set 26MHz clock, but clk_rk3568 will return error in this case. Apparently, SDHCI silently ignores the error and as a result eMMC initialization fails. Add 25 MHz and 26 MHz clk rates for sdmmc and emmc on rk3568 to fix that. Signed-off-by: Vasily Khoruzhick Reviewed-by: Kever Yang --- drivers/clk/rockchip/clk_rk3568.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/rockchip/clk_rk3568.c b/drivers/clk/rockchip/clk_rk3568.c index d5e45e7602c..99c195b3afe 100644 --- a/drivers/clk/rockchip/clk_rk3568.c +++ b/drivers/clk/rockchip/clk_rk3568.c @@ -1442,6 +1442,7 @@ static ulong rk3568_sdmmc_set_clk(struct rk3568_clk_priv *priv, switch (rate) { case OSC_HZ: case 26 * MHz: + case 25 * MHz: src_clk = CLK_SDMMC_SEL_24M; break; case 400 * MHz: @@ -1631,6 +1632,8 @@ static ulong rk3568_emmc_set_clk(struct rk3568_clk_priv *priv, ulong rate) switch (rate) { case OSC_HZ: + case 26 * MHz: + case 25 * MHz: src_clk = CCLK_EMMC_SEL_24M; break; case 52 * MHz: -- cgit v1.2.3 From 1de76a4535a2d617adeb05070d265e2826af3ad6 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 17 Jan 2023 15:08:15 +0100 Subject: arm64: a37xx: pinctrl: probe after binding Currently, pinctrl drivers are getting probed during post-bind, however that is being reverted, and on A37XX pinctrl driver is the one that registers the GPIO driver during the probe. So, if the pinctrl driver doesn't get probed GPIO-s won't get registered and thus they cannot be used. This is a problem on the Methode eDPU as it just uses SB pins as GPIO-s and without them being registered networking won't work as it only has one SFP slot and the TX disable GPIO is on the SB controller. So, lets just add a flag only to A37XX driver to probe after binding in order for the GPIO driver to always get registered. Signed-off-by: Robert Marko Reviewed-by: Simon Glass --- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 25fbe39abd1..1be6252227d 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -745,6 +745,19 @@ static int armada_37xx_pinctrl_probe(struct udevice *dev) return 0; } +static int armada_37xx_pinctrl_bind(struct udevice *dev) +{ + /* + * Make sure that the pinctrl driver gets probed after binding + * as on A37XX the pinctrl driver is the one that is also + * registering the GPIO one during probe, so if its not probed + * GPIO-s are not registered as well. + */ + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); + + return 0; +} + static const struct udevice_id armada_37xx_pinctrl_of_match[] = { { .compatible = "marvell,armada3710-sb-pinctrl", @@ -762,6 +775,7 @@ U_BOOT_DRIVER(armada_37xx_pinctrl) = { .id = UCLASS_PINCTRL, .of_match = of_match_ptr(armada_37xx_pinctrl_of_match), .probe = armada_37xx_pinctrl_probe, + .bind = armada_37xx_pinctrl_bind, .priv_auto = sizeof(struct armada_37xx_pinctrl), .ops = &armada_37xx_pinctrl_ops, }; -- cgit v1.2.3 From d22c69524966b6d65d98476acb7fda827207e10d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 22 Feb 2023 01:39:18 +0100 Subject: sandbox: fix building with CONFIG_SPL_TIMER=y Building sandbox_defconfig with CONFIG_SPL_TIMER=y results in an error include/dm/platdata.h:63:33: error: static assertion failed: "Cannot use U_BOOT_DRVINFO with of-platdata. Please use devicetree instead" Add a missing condition in the sandbox driver. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/timer/sandbox_timer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index c846bfb9f12..1da7e0c3a76 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -66,6 +66,8 @@ U_BOOT_DRIVER(sandbox_timer) = { }; /* This is here in case we don't have a device tree */ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) U_BOOT_DRVINFO(sandbox_timer_non_fdt) = { .name = "sandbox_timer", }; +#endif -- cgit v1.2.3 From 4042ce73c8bee9077d80a42b27aa21f98636b780 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 25 Jan 2023 19:40:16 +0100 Subject: usb: move CONFIG_USB_HUB_DEBOUNCE_TIMEOUT to USB This configuration setting is only relevant if the board supports USB. It should not be in the main menu but in the USB menu. The setting is only relevant in USB host mode. Fixes: 5454dea3137d ("usb: hub: allow to increase HUB_DEBOUNCE_TIMEOUT") Signed-off-by: Heinrich Schuchardt Reviewed-by: Marek Vasut Reviewed-by: Patrick Delaunay --- drivers/usb/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index ebe6bf94981..94fb32d107c 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -115,6 +115,17 @@ config USB_ONBOARD_HUB power regulator. An example for such a hub is the Microchip USB2514B. +config USB_HUB_DEBOUNCE_TIMEOUT + int "Timeout in milliseconds for USB HUB connection" + default 1000 + help + Value in milliseconds of the USB connection timeout, the max delay to + wait the hub port status to be connected steadily after being powered + off and powered on in the usb hub driver. + This define allows to increase the HUB_DEBOUNCE_TIMEOUT default + value = 1s because some usb device needs around 1.5s to be initialized + and a 2s value should solve detection issue on problematic USB keys. + if USB_KEYBOARD config USB_KEYBOARD_FN_KEYS -- cgit v1.2.3 From 6ddffa89cb8396bf043adcc5090e8b1a9b1d0246 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 27 Feb 2023 23:49:27 +0100 Subject: mmc: renesas-sdhi: Always configure default SDnH clock rate to 800 MHz The prior stage bootloader might have left the SDnCKCR register in completely arbitrary state before passing control to U-Boot, which includes the register being populated with incorrect values. Currently the SDHI driver will attempt to use clock framework to configure SDn clock, which may fail in case SDnCKCR contains invalid values for the SDnH clock, because the clock framework would not be able to determine SDnH clock rate and would get -EINVAL instead, which in turn would not allow the clock framework to determine the correct SDn clock divider ratio. This failure occurs specifically in case SDnCKCR reads back 0x209 . Correct the problem by first setting default SDnH clock rate to 800 MHz, thus assuring the SDnCKCR SDnH bits are correct, and only afterward set up the SDn clock rate to default 200 MHz. Note that the SDHI driver may reconfigure SDnH clock later based on IOS settings obtained from the attached card, the 800 MHz set up here is only the default value. Signed-off-by: Marek Vasut --- drivers/mmc/renesas-sdhi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 4a1accebfcb..2473261f3c4 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -977,8 +977,16 @@ static int renesas_sdhi_probe(struct udevice *dev) /* optional SDnH clock */ ret = clk_get_by_name(dev, "clkh", &priv->clkh); - if (ret < 0) + if (ret < 0) { dev_dbg(dev, "failed to get clkh\n"); + } else { + ret = clk_set_rate(&priv->clkh, 800000000); + if (ret < 0) { + dev_err(dev, "failed to set rate for SDnH clock\n"); + clk_free(&priv->clk); + return ret; + } + } /* set to max rate */ ret = clk_set_rate(&priv->clk, 200000000); -- cgit v1.2.3 From f20a61af4209785721bdee96131785d9dc24698d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 27 Feb 2023 23:49:28 +0100 Subject: mmc: renesas-sdhi: Add proper probe error fail path In case one of the calls in probe fail, trigger a fail path and undo all the steps done in probe until the point of failure. The current implementation failed to stop controller clock and free claimed clock, so fix that. Furthermore, print return code in error prints for easier debugging. Signed-off-by: Marek Vasut --- drivers/mmc/renesas-sdhi.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 2473261f3c4..34119f949aa 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -982,37 +982,45 @@ static int renesas_sdhi_probe(struct udevice *dev) } else { ret = clk_set_rate(&priv->clkh, 800000000); if (ret < 0) { - dev_err(dev, "failed to set rate for SDnH clock\n"); - clk_free(&priv->clk); - return ret; + dev_err(dev, "failed to set rate for SDnH clock (%d)\n", ret); + goto err_clk; } } /* set to max rate */ ret = clk_set_rate(&priv->clk, 200000000); if (ret < 0) { - dev_err(dev, "failed to set rate for host clock\n"); - clk_free(&priv->clk); - return ret; + dev_err(dev, "failed to set rate for SDn clock (%d)\n", ret); + goto err_clkh; } ret = clk_enable(&priv->clk); if (ret) { - dev_err(dev, "failed to enable host clock\n"); - return ret; + dev_err(dev, "failed to enable SDn clock (%d)\n", ret); + goto err_clkh; } priv->quirks = quirks; ret = tmio_sd_probe(dev, quirks); + if (ret) + goto err_tmio_probe; renesas_sdhi_filter_caps(dev); #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) - if (!ret && (priv->caps & TMIO_SD_CAP_RCAR_UHS)) + if (priv->caps & TMIO_SD_CAP_RCAR_UHS) renesas_sdhi_reset_tuning(priv); #endif + return 0; + +err_tmio_probe: + clk_disable(&priv->clk); +err_clkh: + clk_free(&priv->clkh); +err_clk: + clk_free(&priv->clk); return ret; } -- cgit v1.2.3 From 3efdf01fa49a24cc2f1139d67f2e1e5cd43cf882 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 28 Feb 2023 22:16:02 +0100 Subject: clk: renesas: Always select DM_RESET to prevent inobvious failure of rst_gen3 subdriver The CLK_RCAR_GEN3 registers two subdrivers, clk_gen3 and rst_gen3. The former depends on the clock framework, which is always enabled in this context of clock framework driver, while the later depends on reset framework which may not always be enabled. Ensure the reset framework is also always enabled to prevent inobvious early boot time bind failure of the CPG driver, which leads to system showing no activity and is difficult to debug. Note that one possible approach to debug this is to use CONFIG_DEBUG_UART and add debug printascii()s into the drivers/clk/renesas/clk-rcar-gen3.c . Signed-off-by: Marek Vasut --- drivers/clk/renesas/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 1686410d6d3..d58e897ca1b 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -49,6 +49,7 @@ config CLK_RCAR_GEN3 def_bool y if RCAR_GEN3 depends on CLK_RENESAS select CLK_RCAR_CPG_LIB + select DM_RESET help Enable this to support the clocks on Renesas RCar Gen3 SoC. -- cgit v1.2.3 From ce4681721570494574beff5f47df3d35d5e9aa10 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 28 Feb 2023 07:25:53 +0100 Subject: pinctrl: renesas: Drop non-existent PFC info table entries Remove PFC info table entries which are never instantiated, since there are no drivers for those. No functional change. Signed-off-by: Marek Vasut --- drivers/pinctrl/renesas/sh_pfc.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/renesas/sh_pfc.h b/drivers/pinctrl/renesas/sh_pfc.h index 59d447ead5c..0ab743e80d5 100644 --- a/drivers/pinctrl/renesas/sh_pfc.h +++ b/drivers/pinctrl/renesas/sh_pfc.h @@ -309,21 +309,6 @@ extern const struct sh_pfc_soc_info r8a77980_pinmux_info; extern const struct sh_pfc_soc_info r8a77990_pinmux_info; extern const struct sh_pfc_soc_info r8a77995_pinmux_info; extern const struct sh_pfc_soc_info r8a779a0_pinmux_info; -extern const struct sh_pfc_soc_info r8a779f0_pinmux_info; -extern const struct sh_pfc_soc_info r8a779g0_pinmux_info; -extern const struct sh_pfc_soc_info sh7203_pinmux_info; -extern const struct sh_pfc_soc_info sh7264_pinmux_info; -extern const struct sh_pfc_soc_info sh7269_pinmux_info; -extern const struct sh_pfc_soc_info sh73a0_pinmux_info; -extern const struct sh_pfc_soc_info sh7720_pinmux_info; -extern const struct sh_pfc_soc_info sh7722_pinmux_info; -extern const struct sh_pfc_soc_info sh7723_pinmux_info; -extern const struct sh_pfc_soc_info sh7724_pinmux_info; -extern const struct sh_pfc_soc_info sh7734_pinmux_info; -extern const struct sh_pfc_soc_info sh7757_pinmux_info; -extern const struct sh_pfc_soc_info sh7785_pinmux_info; -extern const struct sh_pfc_soc_info sh7786_pinmux_info; -extern const struct sh_pfc_soc_info shx3_pinmux_info; /* ----------------------------------------------------------------------------- * Helper macros to create pin and port lists -- cgit v1.2.3 From b791b9cf5500b589df5f8b227a6320bf34ba081c Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Mon, 27 Feb 2023 23:58:46 +0100 Subject: sysinfo: rcar3: Fix Draak and Eagle board code Correct the board code ID based on the hardware documentation Reviewed-by: Marek Vasut Signed-off-by: Tam Nguyen Signed-off-by: Hai Pham Signed-off-by: Marek Vasut --- drivers/sysinfo/rcar3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/sysinfo/rcar3.c b/drivers/sysinfo/rcar3.c index c2f4ddfbbe3..c0afc92f675 100644 --- a/drivers/sysinfo/rcar3.c +++ b/drivers/sysinfo/rcar3.c @@ -16,12 +16,12 @@ #define BOARD_SALVATOR_X 0x0 #define BOARD_KRIEK 0x1 #define BOARD_STARTER_KIT 0x2 +#define BOARD_EAGLE 0x3 #define BOARD_SALVATOR_XS 0x4 +#define BOARD_DRAAK 0x7 #define BOARD_EBISU 0x8 #define BOARD_STARTER_KIT_PRE 0xB #define BOARD_EBISU_4D 0xD -#define BOARD_DRAAK 0xE -#define BOARD_EAGLE 0xF /** * struct sysinfo_rcar_priv - sysinfo private data -- cgit v1.2.3 From c8eaebb426fed1bbb7020566486b4a8f4eb0f159 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Mon, 27 Feb 2023 23:58:47 +0100 Subject: ARM: dts: renesas: Enable sysinfo on R-Car V3H Condor/Condor-I Add new sysinfo IDs for R-Car V3H Condor/Condor-I . Enable support for sysinfo on R-Car V3H Condor/Condor-I. The sysinfo is used e.g. to access and decode board-specific information and then in turn used by board-info to print those information. Reviewed-by: Marek Vasut Signed-off-by: Tam Nguyen Signed-off-by: Hai Pham Signed-off-by: Marek Vasut [Marek: Drop compatible from I2C node, this is in r8a77980.dtsi already. Drop status = "okay" from EEPROM node. Add dts: tag. Update the commit message, note the new sysinfo IDs. Fix Kconfig EEPROM address to be 0x50 and match the DT, sync config.] --- drivers/sysinfo/rcar3.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/sysinfo/rcar3.c b/drivers/sysinfo/rcar3.c index c0afc92f675..7b127986da7 100644 --- a/drivers/sysinfo/rcar3.c +++ b/drivers/sysinfo/rcar3.c @@ -18,10 +18,12 @@ #define BOARD_STARTER_KIT 0x2 #define BOARD_EAGLE 0x3 #define BOARD_SALVATOR_XS 0x4 +#define BOARD_CONDOR 0x6 #define BOARD_DRAAK 0x7 #define BOARD_EBISU 0x8 #define BOARD_STARTER_KIT_PRE 0xB #define BOARD_EBISU_4D 0xD +#define BOARD_CONDOR_I 0x10 /** * struct sysinfo_rcar_priv - sysinfo private data @@ -65,6 +67,7 @@ static void sysinfo_rcar_parse(struct sysinfo_rcar_priv *priv) const u8 board_rev = priv->val & BOARD_REV_MASK; bool salvator_xs = false; bool ebisu_4d = false; + bool condor_i = false; char rev_major = '?'; char rev_minor = '?'; @@ -138,6 +141,18 @@ static void sysinfo_rcar_parse(struct sysinfo_rcar_priv *priv) "Renesas Kriek board rev %c.%c", rev_major, rev_minor); return; + case BOARD_CONDOR_I: + condor_i = true; + fallthrough; + case BOARD_CONDOR: + if (!board_rev) { /* Only rev 0 is valid */ + rev_major = '1'; + rev_minor = '0'; + } + snprintf(priv->boardmodel, sizeof(priv->boardmodel), + "Renesas Condor%s board rev %c.%c", + condor_i ? "-I" : "", rev_major, rev_minor); + return; default: snprintf(priv->boardmodel, sizeof(priv->boardmodel), "Renesas -Unknown- board rev ?.?"); -- cgit v1.2.3