1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2022 Marek Vasut <marex@denx.de> 4 */ 5 6#include <common.h> 7#include <asm/io.h> 8#include <clk.h> 9#include <clk-uclass.h> 10#include <dm.h> 11#include <dm/device.h> 12#include <dm/device_compat.h> 13#include <dm/device-internal.h> 14#include <dm/lists.h> 15#include <linux/bitfield.h> 16#include <linux/delay.h> 17#include <linux/iopoll.h> 18#include <power-domain-uclass.h> 19 20#include <dt-bindings/power/imx8mp-power.h> 21 22#define GPR_REG0 0x0 23#define PCIE_CLOCK_MODULE_EN BIT(0) 24#define USB_CLOCK_MODULE_EN BIT(1) 25#define PCIE_PHY_APB_RST BIT(4) 26#define PCIE_PHY_INIT_RST BIT(5) 27#define GPR_REG1 0x4 28#define PLL_LOCK BIT(13) 29#define GPR_REG2 0x8 30#define P_PLL_MASK GENMASK(5, 0) 31#define M_PLL_MASK GENMASK(15, 6) 32#define S_PLL_MASK GENMASK(18, 16) 33#define GPR_REG3 0xc 34#define PLL_CKE BIT(17) 35#define PLL_RST BIT(31) 36 37struct imx8mp_hsiomix_priv { 38 void __iomem *base; 39 struct clk clk_usb; 40 struct clk clk_pcie; 41 struct power_domain pd_bus; 42 struct power_domain pd_usb; 43 struct power_domain pd_pcie; 44 struct power_domain pd_usb_phy1; 45 struct power_domain pd_usb_phy2; 46 struct power_domain pd_pcie_phy; 47}; 48 49static int imx8mp_hsiomix_set(struct power_domain *power_domain, bool power_on) 50{ 51 struct udevice *dev = power_domain->dev; 52 struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev); 53 struct power_domain *domain = NULL; 54 struct clk *clk = NULL; 55 u32 gpr_reg0_bits = 0; 56 int ret; 57 58 switch (power_domain->id) { 59 case IMX8MP_HSIOBLK_PD_USB: 60 domain = &priv->pd_usb; 61 clk = &priv->clk_usb; 62 gpr_reg0_bits |= USB_CLOCK_MODULE_EN; 63 break; 64 case IMX8MP_HSIOBLK_PD_USB_PHY1: 65 domain = &priv->pd_usb_phy1; 66 break; 67 case IMX8MP_HSIOBLK_PD_USB_PHY2: 68 domain = &priv->pd_usb_phy2; 69 break; 70 case IMX8MP_HSIOBLK_PD_PCIE: 71 domain = &priv->pd_pcie; 72 clk = &priv->clk_pcie; 73 gpr_reg0_bits |= PCIE_CLOCK_MODULE_EN; 74 break; 75 case IMX8MP_HSIOBLK_PD_PCIE_PHY: 76 domain = &priv->pd_pcie_phy; 77 /* Bits to deassert PCIe PHY reset */ 78 gpr_reg0_bits |= PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST; 79 break; 80 default: 81 dev_err(dev, "unknown power domain id: %ld\n", 82 power_domain->id); 83 return -EINVAL; 84 } 85 86 if (power_on) { 87 ret = power_domain_on(&priv->pd_bus); 88 if (ret) 89 return ret; 90 91 ret = power_domain_on(domain); 92 if (ret) 93 goto err_pd; 94 95 if (clk) { 96 ret = clk_enable(clk); 97 if (ret) 98 goto err_clk; 99 } 100 101 if (gpr_reg0_bits) 102 setbits_le32(priv->base + GPR_REG0, gpr_reg0_bits); 103 } else { 104 if (gpr_reg0_bits) 105 clrbits_le32(priv->base + GPR_REG0, gpr_reg0_bits); 106 107 if (clk) 108 clk_disable(clk); 109 110 power_domain_off(domain); 111 power_domain_off(&priv->pd_bus); 112 } 113 114 return 0; 115 116err_clk: 117 power_domain_off(domain); 118err_pd: 119 power_domain_off(&priv->pd_bus); 120 return ret; 121} 122 123static int imx8mp_hsiomix_on(struct power_domain *power_domain) 124{ 125 return imx8mp_hsiomix_set(power_domain, true); 126} 127 128static int imx8mp_hsiomix_off(struct power_domain *power_domain) 129{ 130 return imx8mp_hsiomix_set(power_domain, false); 131} 132 133static int imx8mp_hsiomix_of_xlate(struct power_domain *power_domain, 134 struct ofnode_phandle_args *args) 135{ 136 power_domain->id = args->args[0]; 137 138 return 0; 139} 140 141static int hsio_pll_clk_enable(struct clk *clk) 142{ 143 void *base = (void *)dev_get_driver_data(clk->dev); 144 u32 val; 145 int ret; 146 147 /* Setup HSIO PLL as 100 MHz output clock */ 148 clrsetbits_le32(base + GPR_REG2, 149 P_PLL_MASK | M_PLL_MASK | S_PLL_MASK, 150 FIELD_PREP(P_PLL_MASK, 12) | 151 FIELD_PREP(M_PLL_MASK, 800) | 152 FIELD_PREP(S_PLL_MASK, 4)); 153 154 /* de-assert PLL reset */ 155 setbits_le32(base + GPR_REG3, PLL_RST); 156 157 /* enable PLL */ 158 setbits_le32(base + GPR_REG3, PLL_CKE); 159 160 /* Check if PLL is locked */ 161 ret = readl_poll_sleep_timeout(base + GPR_REG1, val, 162 val & PLL_LOCK, 10, 100000); 163 if (ret) 164 dev_err(clk->dev, "failed to lock HSIO PLL\n"); 165 166 return ret; 167} 168 169static int hsio_pll_clk_disable(struct clk *clk) 170{ 171 void *base = (void *)dev_get_driver_data(clk->dev); 172 173 clrbits_le32(base + GPR_REG3, PLL_CKE | PLL_RST); 174 175 return 0; 176} 177 178static const struct clk_ops hsio_pll_clk_ops = { 179 .enable = hsio_pll_clk_enable, 180 .disable = hsio_pll_clk_disable, 181}; 182 183U_BOOT_DRIVER(hsio_pll) = { 184 .name = "hsio-pll", 185 .id = UCLASS_CLK, 186 .ops = &hsio_pll_clk_ops, 187}; 188 189int imx8mp_hsiomix_bind(struct udevice *dev) 190{ 191 struct driver *drv; 192 193 drv = lists_driver_lookup_name("hsio-pll"); 194 if (!drv) 195 return -ENOENT; 196 197 return device_bind_with_driver_data(dev, drv, "hsio-pll", 198 (ulong)dev_read_addr_ptr(dev), 199 dev_ofnode(dev), NULL); 200} 201 202static int imx8mp_hsiomix_probe(struct udevice *dev) 203{ 204 struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev); 205 int ret; 206 207 priv->base = dev_read_addr_ptr(dev); 208 209 ret = clk_get_by_name(dev, "usb", &priv->clk_usb); 210 if (ret < 0) 211 return ret; 212 213 ret = clk_get_by_name(dev, "pcie", &priv->clk_pcie); 214 if (ret < 0) 215 return ret; 216 217 ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus"); 218 if (ret < 0) 219 return ret; 220 221 ret = power_domain_get_by_name(dev, &priv->pd_usb, "usb"); 222 if (ret < 0) 223 goto err_pd_usb; 224 225 ret = power_domain_get_by_name(dev, &priv->pd_usb_phy1, "usb-phy1"); 226 if (ret < 0) 227 goto err_pd_usb_phy1; 228 229 ret = power_domain_get_by_name(dev, &priv->pd_usb_phy2, "usb-phy2"); 230 if (ret < 0) 231 goto err_pd_usb_phy2; 232 233 ret = power_domain_get_by_name(dev, &priv->pd_pcie, "pcie"); 234 if (ret < 0) 235 goto err_pd_pcie; 236 237 ret = power_domain_get_by_name(dev, &priv->pd_pcie_phy, "pcie-phy"); 238 if (ret < 0) 239 goto err_pd_pcie_phy; 240 241 return 0; 242 243err_pd_pcie_phy: 244 power_domain_free(&priv->pd_pcie); 245err_pd_pcie: 246 power_domain_free(&priv->pd_usb_phy2); 247err_pd_usb_phy2: 248 power_domain_free(&priv->pd_usb_phy1); 249err_pd_usb_phy1: 250 power_domain_free(&priv->pd_usb); 251err_pd_usb: 252 power_domain_free(&priv->pd_bus); 253 return ret; 254} 255 256static const struct udevice_id imx8mp_hsiomix_ids[] = { 257 { .compatible = "fsl,imx8mp-hsio-blk-ctrl" }, 258 { } 259}; 260 261struct power_domain_ops imx8mp_hsiomix_ops = { 262 .on = imx8mp_hsiomix_on, 263 .off = imx8mp_hsiomix_off, 264 .of_xlate = imx8mp_hsiomix_of_xlate, 265}; 266 267U_BOOT_DRIVER(imx8mp_hsiomix) = { 268 .name = "imx8mp_hsiomix", 269 .id = UCLASS_POWER_DOMAIN, 270 .of_match = imx8mp_hsiomix_ids, 271 .probe = imx8mp_hsiomix_probe, 272 .bind = imx8mp_hsiomix_bind, 273 .priv_auto = sizeof(struct imx8mp_hsiomix_priv), 274 .ops = &imx8mp_hsiomix_ops, 275}; 276