David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 2 | /* |
| 3 | * Copyright 2017 Impinj, Inc |
| 4 | * Author: Andrey Smirnov <andrew.smirnov@gmail.com> |
| 5 | * |
| 6 | * Based on the code of analogus driver: |
| 7 | * |
| 8 | * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 11 | #include <linux/clk.h> |
| 12 | #include <linux/of_device.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 13 | #include <linux/platform_device.h> |
| 14 | #include <linux/pm_domain.h> |
| 15 | #include <linux/regmap.h> |
| 16 | #include <linux/regulator/consumer.h> |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 17 | #include <linux/sizes.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 18 | #include <dt-bindings/power/imx7-power.h> |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 19 | #include <dt-bindings/power/imx8mq-power.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 20 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 21 | #define GPC_LPCR_A_CORE_BSC 0x000 |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 22 | |
| 23 | #define GPC_PGC_CPU_MAPPING 0x0ec |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 24 | |
| 25 | #define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) |
| 26 | #define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) |
| 27 | #define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) |
| 28 | #define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3) |
| 29 | #define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2) |
| 30 | |
| 31 | #define IMX8M_PCIE2_A53_DOMAIN BIT(15) |
| 32 | #define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) |
| 33 | #define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) |
| 34 | #define IMX8M_DISP_A53_DOMAIN BIT(12) |
| 35 | #define IMX8M_HDMI_A53_DOMAIN BIT(11) |
| 36 | #define IMX8M_VPU_A53_DOMAIN BIT(10) |
| 37 | #define IMX8M_GPU_A53_DOMAIN BIT(9) |
| 38 | #define IMX8M_DDR2_A53_DOMAIN BIT(8) |
| 39 | #define IMX8M_DDR1_A53_DOMAIN BIT(7) |
| 40 | #define IMX8M_OTG2_A53_DOMAIN BIT(5) |
| 41 | #define IMX8M_OTG1_A53_DOMAIN BIT(4) |
| 42 | #define IMX8M_PCIE1_A53_DOMAIN BIT(3) |
| 43 | #define IMX8M_MIPI_A53_DOMAIN BIT(2) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 44 | |
| 45 | #define GPC_PU_PGC_SW_PUP_REQ 0x0f8 |
| 46 | #define GPC_PU_PGC_SW_PDN_REQ 0x104 |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 47 | |
| 48 | #define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) |
| 49 | #define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) |
| 50 | #define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) |
| 51 | #define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) |
| 52 | #define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) |
| 53 | |
| 54 | #define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) |
| 55 | #define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) |
| 56 | #define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) |
| 57 | #define IMX8M_DISP_SW_Pxx_REQ BIT(10) |
| 58 | #define IMX8M_HDMI_SW_Pxx_REQ BIT(9) |
| 59 | #define IMX8M_VPU_SW_Pxx_REQ BIT(8) |
| 60 | #define IMX8M_GPU_SW_Pxx_REQ BIT(7) |
| 61 | #define IMX8M_DDR2_SW_Pxx_REQ BIT(6) |
| 62 | #define IMX8M_DDR1_SW_Pxx_REQ BIT(5) |
| 63 | #define IMX8M_OTG2_SW_Pxx_REQ BIT(3) |
| 64 | #define IMX8M_OTG1_SW_Pxx_REQ BIT(2) |
| 65 | #define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) |
| 66 | #define IMX8M_MIPI_SW_Pxx_REQ BIT(0) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 67 | |
| 68 | #define GPC_M4_PU_PDN_FLG 0x1bc |
| 69 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 70 | #define GPC_PU_PWRHSK 0x1fc |
| 71 | |
| 72 | #define IMX8M_GPU_HSK_PWRDNREQN BIT(6) |
| 73 | #define IMX8M_VPU_HSK_PWRDNREQN BIT(5) |
| 74 | #define IMX8M_DISP_HSK_PWRDNREQN BIT(4) |
| 75 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 76 | /* |
| 77 | * The PGC offset values in Reference Manual |
| 78 | * (Rev. 1, 01/2018 and the older ones) GPC chapter's |
| 79 | * GPC_PGC memory map are incorrect, below offset |
| 80 | * values are from design RTL. |
| 81 | */ |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 82 | #define IMX7_PGC_MIPI 16 |
| 83 | #define IMX7_PGC_PCIE 17 |
| 84 | #define IMX7_PGC_USB_HSIC 20 |
| 85 | |
| 86 | #define IMX8M_PGC_MIPI 16 |
| 87 | #define IMX8M_PGC_PCIE1 17 |
| 88 | #define IMX8M_PGC_OTG1 18 |
| 89 | #define IMX8M_PGC_OTG2 19 |
| 90 | #define IMX8M_PGC_DDR1 21 |
| 91 | #define IMX8M_PGC_GPU 23 |
| 92 | #define IMX8M_PGC_VPU 24 |
| 93 | #define IMX8M_PGC_DISP 26 |
| 94 | #define IMX8M_PGC_MIPI_CSI1 27 |
| 95 | #define IMX8M_PGC_MIPI_CSI2 28 |
| 96 | #define IMX8M_PGC_PCIE2 29 |
| 97 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 98 | #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) |
| 99 | #define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) |
| 100 | |
| 101 | #define GPC_PGC_CTRL_PCR BIT(0) |
| 102 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 103 | #define GPC_CLK_MAX 6 |
| 104 | |
| 105 | struct imx_pgc_domain { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 106 | struct generic_pm_domain genpd; |
| 107 | struct regmap *regmap; |
| 108 | struct regulator *regulator; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 109 | struct clk *clk[GPC_CLK_MAX]; |
| 110 | int num_clks; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 111 | |
| 112 | unsigned int pgc; |
| 113 | |
| 114 | const struct { |
| 115 | u32 pxx; |
| 116 | u32 map; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 117 | u32 hsk; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 118 | } bits; |
| 119 | |
| 120 | const int voltage; |
| 121 | struct device *dev; |
| 122 | }; |
| 123 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 124 | struct imx_pgc_domain_data { |
| 125 | const struct imx_pgc_domain *domains; |
| 126 | size_t domains_num; |
| 127 | const struct regmap_access_table *reg_access_table; |
| 128 | }; |
| 129 | |
| 130 | static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 131 | bool on) |
| 132 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 133 | struct imx_pgc_domain *domain = container_of(genpd, |
| 134 | struct imx_pgc_domain, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 135 | genpd); |
| 136 | unsigned int offset = on ? |
| 137 | GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ; |
| 138 | const bool enable_power_control = !on; |
| 139 | const bool has_regulator = !IS_ERR(domain->regulator); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 140 | int i, ret = 0; |
| 141 | u32 pxx_req; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 142 | |
| 143 | regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, |
| 144 | domain->bits.map, domain->bits.map); |
| 145 | |
| 146 | if (has_regulator && on) { |
| 147 | ret = regulator_enable(domain->regulator); |
| 148 | if (ret) { |
| 149 | dev_err(domain->dev, "failed to enable regulator\n"); |
| 150 | goto unmap; |
| 151 | } |
| 152 | } |
| 153 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 154 | /* Enable reset clocks for all devices in the domain */ |
| 155 | for (i = 0; i < domain->num_clks; i++) |
| 156 | clk_prepare_enable(domain->clk[i]); |
| 157 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 158 | if (enable_power_control) |
| 159 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), |
| 160 | GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); |
| 161 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 162 | if (domain->bits.hsk) |
| 163 | regmap_update_bits(domain->regmap, GPC_PU_PWRHSK, |
| 164 | domain->bits.hsk, on ? domain->bits.hsk : 0); |
| 165 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 166 | regmap_update_bits(domain->regmap, offset, |
| 167 | domain->bits.pxx, domain->bits.pxx); |
| 168 | |
| 169 | /* |
| 170 | * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait |
| 171 | * for PUP_REQ/PDN_REQ bit to be cleared |
| 172 | */ |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 173 | ret = regmap_read_poll_timeout(domain->regmap, offset, pxx_req, |
| 174 | !(pxx_req & domain->bits.pxx), |
| 175 | 0, USEC_PER_MSEC); |
| 176 | if (ret) { |
| 177 | dev_err(domain->dev, "failed to command PGC\n"); |
| 178 | /* |
| 179 | * If we were in a process of enabling a |
| 180 | * domain and failed we might as well disable |
| 181 | * the regulator we just enabled. And if it |
| 182 | * was the opposite situation and we failed to |
| 183 | * power down -- keep the regulator on |
| 184 | */ |
| 185 | on = !on; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | if (enable_power_control) |
| 189 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), |
| 190 | GPC_PGC_CTRL_PCR, 0); |
| 191 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 192 | /* Disable reset clocks for all devices in the domain */ |
| 193 | for (i = 0; i < domain->num_clks; i++) |
| 194 | clk_disable_unprepare(domain->clk[i]); |
| 195 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 196 | if (has_regulator && !on) { |
| 197 | int err; |
| 198 | |
| 199 | err = regulator_disable(domain->regulator); |
| 200 | if (err) |
| 201 | dev_err(domain->dev, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 202 | "failed to disable regulator: %d\n", err); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 203 | /* Preserve earlier error code */ |
| 204 | ret = ret ?: err; |
| 205 | } |
| 206 | unmap: |
| 207 | regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, |
| 208 | domain->bits.map, 0); |
| 209 | return ret; |
| 210 | } |
| 211 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 212 | static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 213 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 214 | return imx_gpc_pu_pgc_sw_pxx_req(genpd, true); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 215 | } |
| 216 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 217 | static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 218 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 219 | return imx_gpc_pu_pgc_sw_pxx_req(genpd, false); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 220 | } |
| 221 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 222 | static const struct imx_pgc_domain imx7_pgc_domains[] = { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 223 | [IMX7_POWER_DOMAIN_MIPI_PHY] = { |
| 224 | .genpd = { |
| 225 | .name = "mipi-phy", |
| 226 | }, |
| 227 | .bits = { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 228 | .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, |
| 229 | .map = IMX7_MIPI_PHY_A_CORE_DOMAIN, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 230 | }, |
| 231 | .voltage = 1000000, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 232 | .pgc = IMX7_PGC_MIPI, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 233 | }, |
| 234 | |
| 235 | [IMX7_POWER_DOMAIN_PCIE_PHY] = { |
| 236 | .genpd = { |
| 237 | .name = "pcie-phy", |
| 238 | }, |
| 239 | .bits = { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 240 | .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, |
| 241 | .map = IMX7_PCIE_PHY_A_CORE_DOMAIN, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 242 | }, |
| 243 | .voltage = 1000000, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 244 | .pgc = IMX7_PGC_PCIE, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 245 | }, |
| 246 | |
| 247 | [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { |
| 248 | .genpd = { |
| 249 | .name = "usb-hsic-phy", |
| 250 | }, |
| 251 | .bits = { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 252 | .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, |
| 253 | .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 254 | }, |
| 255 | .voltage = 1200000, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 256 | .pgc = IMX7_PGC_USB_HSIC, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 257 | }, |
| 258 | }; |
| 259 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 260 | static const struct regmap_range imx7_yes_ranges[] = { |
| 261 | regmap_reg_range(GPC_LPCR_A_CORE_BSC, |
| 262 | GPC_M4_PU_PDN_FLG), |
| 263 | regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_MIPI), |
| 264 | GPC_PGC_SR(IMX7_PGC_MIPI)), |
| 265 | regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_PCIE), |
| 266 | GPC_PGC_SR(IMX7_PGC_PCIE)), |
| 267 | regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_USB_HSIC), |
| 268 | GPC_PGC_SR(IMX7_PGC_USB_HSIC)), |
| 269 | }; |
| 270 | |
| 271 | static const struct regmap_access_table imx7_access_table = { |
| 272 | .yes_ranges = imx7_yes_ranges, |
| 273 | .n_yes_ranges = ARRAY_SIZE(imx7_yes_ranges), |
| 274 | }; |
| 275 | |
| 276 | static const struct imx_pgc_domain_data imx7_pgc_domain_data = { |
| 277 | .domains = imx7_pgc_domains, |
| 278 | .domains_num = ARRAY_SIZE(imx7_pgc_domains), |
| 279 | .reg_access_table = &imx7_access_table, |
| 280 | }; |
| 281 | |
| 282 | static const struct imx_pgc_domain imx8m_pgc_domains[] = { |
| 283 | [IMX8M_POWER_DOMAIN_MIPI] = { |
| 284 | .genpd = { |
| 285 | .name = "mipi", |
| 286 | }, |
| 287 | .bits = { |
| 288 | .pxx = IMX8M_MIPI_SW_Pxx_REQ, |
| 289 | .map = IMX8M_MIPI_A53_DOMAIN, |
| 290 | }, |
| 291 | .pgc = IMX8M_PGC_MIPI, |
| 292 | }, |
| 293 | |
| 294 | [IMX8M_POWER_DOMAIN_PCIE1] = { |
| 295 | .genpd = { |
| 296 | .name = "pcie1", |
| 297 | }, |
| 298 | .bits = { |
| 299 | .pxx = IMX8M_PCIE1_SW_Pxx_REQ, |
| 300 | .map = IMX8M_PCIE1_A53_DOMAIN, |
| 301 | }, |
| 302 | .pgc = IMX8M_PGC_PCIE1, |
| 303 | }, |
| 304 | |
| 305 | [IMX8M_POWER_DOMAIN_USB_OTG1] = { |
| 306 | .genpd = { |
| 307 | .name = "usb-otg1", |
| 308 | }, |
| 309 | .bits = { |
| 310 | .pxx = IMX8M_OTG1_SW_Pxx_REQ, |
| 311 | .map = IMX8M_OTG1_A53_DOMAIN, |
| 312 | }, |
| 313 | .pgc = IMX8M_PGC_OTG1, |
| 314 | }, |
| 315 | |
| 316 | [IMX8M_POWER_DOMAIN_USB_OTG2] = { |
| 317 | .genpd = { |
| 318 | .name = "usb-otg2", |
| 319 | }, |
| 320 | .bits = { |
| 321 | .pxx = IMX8M_OTG2_SW_Pxx_REQ, |
| 322 | .map = IMX8M_OTG2_A53_DOMAIN, |
| 323 | }, |
| 324 | .pgc = IMX8M_PGC_OTG2, |
| 325 | }, |
| 326 | |
| 327 | [IMX8M_POWER_DOMAIN_DDR1] = { |
| 328 | .genpd = { |
| 329 | .name = "ddr1", |
| 330 | }, |
| 331 | .bits = { |
| 332 | .pxx = IMX8M_DDR1_SW_Pxx_REQ, |
| 333 | .map = IMX8M_DDR2_A53_DOMAIN, |
| 334 | }, |
| 335 | .pgc = IMX8M_PGC_DDR1, |
| 336 | }, |
| 337 | |
| 338 | [IMX8M_POWER_DOMAIN_GPU] = { |
| 339 | .genpd = { |
| 340 | .name = "gpu", |
| 341 | }, |
| 342 | .bits = { |
| 343 | .pxx = IMX8M_GPU_SW_Pxx_REQ, |
| 344 | .map = IMX8M_GPU_A53_DOMAIN, |
| 345 | .hsk = IMX8M_GPU_HSK_PWRDNREQN, |
| 346 | }, |
| 347 | .pgc = IMX8M_PGC_GPU, |
| 348 | }, |
| 349 | |
| 350 | [IMX8M_POWER_DOMAIN_VPU] = { |
| 351 | .genpd = { |
| 352 | .name = "vpu", |
| 353 | }, |
| 354 | .bits = { |
| 355 | .pxx = IMX8M_VPU_SW_Pxx_REQ, |
| 356 | .map = IMX8M_VPU_A53_DOMAIN, |
| 357 | .hsk = IMX8M_VPU_HSK_PWRDNREQN, |
| 358 | }, |
| 359 | .pgc = IMX8M_PGC_VPU, |
| 360 | }, |
| 361 | |
| 362 | [IMX8M_POWER_DOMAIN_DISP] = { |
| 363 | .genpd = { |
| 364 | .name = "disp", |
| 365 | }, |
| 366 | .bits = { |
| 367 | .pxx = IMX8M_DISP_SW_Pxx_REQ, |
| 368 | .map = IMX8M_DISP_A53_DOMAIN, |
| 369 | .hsk = IMX8M_DISP_HSK_PWRDNREQN, |
| 370 | }, |
| 371 | .pgc = IMX8M_PGC_DISP, |
| 372 | }, |
| 373 | |
| 374 | [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { |
| 375 | .genpd = { |
| 376 | .name = "mipi-csi1", |
| 377 | }, |
| 378 | .bits = { |
| 379 | .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, |
| 380 | .map = IMX8M_MIPI_CSI1_A53_DOMAIN, |
| 381 | }, |
| 382 | .pgc = IMX8M_PGC_MIPI_CSI1, |
| 383 | }, |
| 384 | |
| 385 | [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { |
| 386 | .genpd = { |
| 387 | .name = "mipi-csi2", |
| 388 | }, |
| 389 | .bits = { |
| 390 | .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, |
| 391 | .map = IMX8M_MIPI_CSI2_A53_DOMAIN, |
| 392 | }, |
| 393 | .pgc = IMX8M_PGC_MIPI_CSI2, |
| 394 | }, |
| 395 | |
| 396 | [IMX8M_POWER_DOMAIN_PCIE2] = { |
| 397 | .genpd = { |
| 398 | .name = "pcie2", |
| 399 | }, |
| 400 | .bits = { |
| 401 | .pxx = IMX8M_PCIE2_SW_Pxx_REQ, |
| 402 | .map = IMX8M_PCIE2_A53_DOMAIN, |
| 403 | }, |
| 404 | .pgc = IMX8M_PGC_PCIE2, |
| 405 | }, |
| 406 | }; |
| 407 | |
| 408 | static const struct regmap_range imx8m_yes_ranges[] = { |
| 409 | regmap_reg_range(GPC_LPCR_A_CORE_BSC, |
| 410 | GPC_PU_PWRHSK), |
| 411 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), |
| 412 | GPC_PGC_SR(IMX8M_PGC_MIPI)), |
| 413 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), |
| 414 | GPC_PGC_SR(IMX8M_PGC_PCIE1)), |
| 415 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG1), |
| 416 | GPC_PGC_SR(IMX8M_PGC_OTG1)), |
| 417 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG2), |
| 418 | GPC_PGC_SR(IMX8M_PGC_OTG2)), |
| 419 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DDR1), |
| 420 | GPC_PGC_SR(IMX8M_PGC_DDR1)), |
| 421 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_GPU), |
| 422 | GPC_PGC_SR(IMX8M_PGC_GPU)), |
| 423 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_VPU), |
| 424 | GPC_PGC_SR(IMX8M_PGC_VPU)), |
| 425 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DISP), |
| 426 | GPC_PGC_SR(IMX8M_PGC_DISP)), |
| 427 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI1), |
| 428 | GPC_PGC_SR(IMX8M_PGC_MIPI_CSI1)), |
| 429 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI2), |
| 430 | GPC_PGC_SR(IMX8M_PGC_MIPI_CSI2)), |
| 431 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE2), |
| 432 | GPC_PGC_SR(IMX8M_PGC_PCIE2)), |
| 433 | }; |
| 434 | |
| 435 | static const struct regmap_access_table imx8m_access_table = { |
| 436 | .yes_ranges = imx8m_yes_ranges, |
| 437 | .n_yes_ranges = ARRAY_SIZE(imx8m_yes_ranges), |
| 438 | }; |
| 439 | |
| 440 | static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { |
| 441 | .domains = imx8m_pgc_domains, |
| 442 | .domains_num = ARRAY_SIZE(imx8m_pgc_domains), |
| 443 | .reg_access_table = &imx8m_access_table, |
| 444 | }; |
| 445 | |
| 446 | static int imx_pgc_get_clocks(struct imx_pgc_domain *domain) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 447 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 448 | int i, ret; |
| 449 | |
| 450 | for (i = 0; ; i++) { |
| 451 | struct clk *clk = of_clk_get(domain->dev->of_node, i); |
| 452 | if (IS_ERR(clk)) |
| 453 | break; |
| 454 | if (i >= GPC_CLK_MAX) { |
| 455 | dev_err(domain->dev, "more than %d clocks\n", |
| 456 | GPC_CLK_MAX); |
| 457 | ret = -EINVAL; |
| 458 | goto clk_err; |
| 459 | } |
| 460 | domain->clk[i] = clk; |
| 461 | } |
| 462 | domain->num_clks = i; |
| 463 | |
| 464 | return 0; |
| 465 | |
| 466 | clk_err: |
| 467 | while (i--) |
| 468 | clk_put(domain->clk[i]); |
| 469 | |
| 470 | return ret; |
| 471 | } |
| 472 | |
| 473 | static void imx_pgc_put_clocks(struct imx_pgc_domain *domain) |
| 474 | { |
| 475 | int i; |
| 476 | |
| 477 | for (i = domain->num_clks - 1; i >= 0; i--) |
| 478 | clk_put(domain->clk[i]); |
| 479 | } |
| 480 | |
| 481 | static int imx_pgc_domain_probe(struct platform_device *pdev) |
| 482 | { |
| 483 | struct imx_pgc_domain *domain = pdev->dev.platform_data; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 484 | int ret; |
| 485 | |
| 486 | domain->dev = &pdev->dev; |
| 487 | |
| 488 | domain->regulator = devm_regulator_get_optional(domain->dev, "power"); |
| 489 | if (IS_ERR(domain->regulator)) { |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 490 | if (PTR_ERR(domain->regulator) != -ENODEV) |
| 491 | return dev_err_probe(domain->dev, PTR_ERR(domain->regulator), |
| 492 | "Failed to get domain's regulator\n"); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 493 | } else if (domain->voltage) { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 494 | regulator_set_voltage(domain->regulator, |
| 495 | domain->voltage, domain->voltage); |
| 496 | } |
| 497 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 498 | ret = imx_pgc_get_clocks(domain); |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 499 | if (ret) |
| 500 | return dev_err_probe(domain->dev, ret, "Failed to get domain's clocks\n"); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 501 | |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 502 | ret = pm_genpd_init(&domain->genpd, NULL, true); |
| 503 | if (ret) { |
| 504 | dev_err(domain->dev, "Failed to init power domain\n"); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 505 | imx_pgc_put_clocks(domain); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 506 | return ret; |
| 507 | } |
| 508 | |
| 509 | ret = of_genpd_add_provider_simple(domain->dev->of_node, |
| 510 | &domain->genpd); |
| 511 | if (ret) { |
| 512 | dev_err(domain->dev, "Failed to add genpd provider\n"); |
| 513 | pm_genpd_remove(&domain->genpd); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 514 | imx_pgc_put_clocks(domain); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 515 | } |
| 516 | |
| 517 | return ret; |
| 518 | } |
| 519 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 520 | static int imx_pgc_domain_remove(struct platform_device *pdev) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 521 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 522 | struct imx_pgc_domain *domain = pdev->dev.platform_data; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 523 | |
| 524 | of_genpd_del_provider(domain->dev->of_node); |
| 525 | pm_genpd_remove(&domain->genpd); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 526 | imx_pgc_put_clocks(domain); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 527 | |
| 528 | return 0; |
| 529 | } |
| 530 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 531 | static const struct platform_device_id imx_pgc_domain_id[] = { |
| 532 | { "imx-pgc-domain", }, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 533 | { }, |
| 534 | }; |
| 535 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 536 | static struct platform_driver imx_pgc_domain_driver = { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 537 | .driver = { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 538 | .name = "imx-pgc", |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 539 | }, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 540 | .probe = imx_pgc_domain_probe, |
| 541 | .remove = imx_pgc_domain_remove, |
| 542 | .id_table = imx_pgc_domain_id, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 543 | }; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 544 | builtin_platform_driver(imx_pgc_domain_driver) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 545 | |
| 546 | static int imx_gpcv2_probe(struct platform_device *pdev) |
| 547 | { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 548 | const struct imx_pgc_domain_data *domain_data = |
| 549 | of_device_get_match_data(&pdev->dev); |
| 550 | |
| 551 | struct regmap_config regmap_config = { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 552 | .reg_bits = 32, |
| 553 | .val_bits = 32, |
| 554 | .reg_stride = 4, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 555 | .rd_table = domain_data->reg_access_table, |
| 556 | .wr_table = domain_data->reg_access_table, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 557 | .max_register = SZ_4K, |
| 558 | }; |
| 559 | struct device *dev = &pdev->dev; |
| 560 | struct device_node *pgc_np, *np; |
| 561 | struct regmap *regmap; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 562 | void __iomem *base; |
| 563 | int ret; |
| 564 | |
| 565 | pgc_np = of_get_child_by_name(dev->of_node, "pgc"); |
| 566 | if (!pgc_np) { |
| 567 | dev_err(dev, "No power domains specified in DT\n"); |
| 568 | return -EINVAL; |
| 569 | } |
| 570 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 571 | base = devm_platform_ioremap_resource(pdev, 0); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 572 | if (IS_ERR(base)) |
| 573 | return PTR_ERR(base); |
| 574 | |
| 575 | regmap = devm_regmap_init_mmio(dev, base, ®map_config); |
| 576 | if (IS_ERR(regmap)) { |
| 577 | ret = PTR_ERR(regmap); |
| 578 | dev_err(dev, "failed to init regmap (%d)\n", ret); |
| 579 | return ret; |
| 580 | } |
| 581 | |
| 582 | for_each_child_of_node(pgc_np, np) { |
| 583 | struct platform_device *pd_pdev; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 584 | struct imx_pgc_domain *domain; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 585 | u32 domain_index; |
| 586 | |
| 587 | ret = of_property_read_u32(np, "reg", &domain_index); |
| 588 | if (ret) { |
| 589 | dev_err(dev, "Failed to read 'reg' property\n"); |
| 590 | of_node_put(np); |
| 591 | return ret; |
| 592 | } |
| 593 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 594 | if (domain_index >= domain_data->domains_num) { |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 595 | dev_warn(dev, |
| 596 | "Domain index %d is out of bounds\n", |
| 597 | domain_index); |
| 598 | continue; |
| 599 | } |
| 600 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 601 | pd_pdev = platform_device_alloc("imx-pgc-domain", |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 602 | domain_index); |
| 603 | if (!pd_pdev) { |
| 604 | dev_err(dev, "Failed to allocate platform device\n"); |
| 605 | of_node_put(np); |
| 606 | return -ENOMEM; |
| 607 | } |
| 608 | |
| 609 | ret = platform_device_add_data(pd_pdev, |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 610 | &domain_data->domains[domain_index], |
| 611 | sizeof(domain_data->domains[domain_index])); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 612 | if (ret) { |
| 613 | platform_device_put(pd_pdev); |
| 614 | of_node_put(np); |
| 615 | return ret; |
| 616 | } |
| 617 | |
| 618 | domain = pd_pdev->dev.platform_data; |
| 619 | domain->regmap = regmap; |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 620 | domain->genpd.power_on = imx_gpc_pu_pgc_sw_pup_req; |
| 621 | domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req; |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 622 | |
| 623 | pd_pdev->dev.parent = dev; |
| 624 | pd_pdev->dev.of_node = np; |
| 625 | |
| 626 | ret = platform_device_add(pd_pdev); |
| 627 | if (ret) { |
| 628 | platform_device_put(pd_pdev); |
| 629 | of_node_put(np); |
| 630 | return ret; |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | return 0; |
| 635 | } |
| 636 | |
| 637 | static const struct of_device_id imx_gpcv2_dt_ids[] = { |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 638 | { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, |
| 639 | { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 640 | { } |
| 641 | }; |
| 642 | |
| 643 | static struct platform_driver imx_gpc_driver = { |
| 644 | .driver = { |
| 645 | .name = "imx-gpcv2", |
| 646 | .of_match_table = imx_gpcv2_dt_ids, |
| 647 | }, |
| 648 | .probe = imx_gpcv2_probe, |
| 649 | }; |
| 650 | builtin_platform_driver(imx_gpc_driver) |