1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 MediaTek Inc. 4 * Author: Ryder Lee <ryder.lee@mediatek.com> 5 */ 6 7 #include <clk.h> 8 #include <common.h> 9 #include <dm.h> 10 #include <power-domain-uclass.h> 11 #include <regmap.h> 12 #include <syscon.h> 13 #include <asm/io.h> 14 #include <asm/processor.h> 15 #include <linux/iopoll.h> 16 17 #include <dt-bindings/power/mt7623-power.h> 18 #include <dt-bindings/power/mt7629-power.h> 19 20 #define SPM_EN (0xb16 << 16 | 0x1) 21 #define SPM_VDE_PWR_CON 0x0210 22 #define SPM_MFG_PWR_CON 0x0214 23 #define SPM_ISP_PWR_CON 0x0238 24 #define SPM_DIS_PWR_CON 0x023c 25 #define SPM_CONN_PWR_CON 0x0280 26 #define SPM_BDP_PWR_CON 0x029c 27 #define SPM_ETH_PWR_CON 0x02a0 28 #define SPM_HIF_PWR_CON 0x02a4 29 #define SPM_IFR_MSC_PWR_CON 0x02a8 30 #define SPM_ETHSYS_PWR_CON 0x2e0 31 #define SPM_HIF0_PWR_CON 0x2e4 32 #define SPM_HIF1_PWR_CON 0x2e8 33 #define SPM_PWR_STATUS 0x60c 34 #define SPM_PWR_STATUS_2ND 0x610 35 36 #define PWR_RST_B_BIT BIT(0) 37 #define PWR_ISO_BIT BIT(1) 38 #define PWR_ON_BIT BIT(2) 39 #define PWR_ON_2ND_BIT BIT(3) 40 #define PWR_CLK_DIS_BIT BIT(4) 41 42 #define PWR_STATUS_CONN BIT(1) 43 #define PWR_STATUS_DISP BIT(3) 44 #define PWR_STATUS_MFG BIT(4) 45 #define PWR_STATUS_ISP BIT(5) 46 #define PWR_STATUS_VDEC BIT(7) 47 #define PWR_STATUS_BDP BIT(14) 48 #define PWR_STATUS_ETH BIT(15) 49 #define PWR_STATUS_HIF BIT(16) 50 #define PWR_STATUS_IFR_MSC BIT(17) 51 #define PWR_STATUS_ETHSYS BIT(24) 52 #define PWR_STATUS_HIF0 BIT(25) 53 #define PWR_STATUS_HIF1 BIT(26) 54 55 /* Infrasys configuration */ 56 #define INFRA_TOPDCM_CTRL 0x10 57 #define INFRA_TOPAXI_PROT_EN 0x220 58 #define INFRA_TOPAXI_PROT_STA1 0x228 59 60 #define DCM_TOP_EN BIT(0) 61 62 enum scp_domain_type { 63 SCPSYS_MT7623, 64 SCPSYS_MT7629, 65 }; 66 67 struct scp_domain; 68 69 struct scp_domain_data { 70 struct scp_domain *scpd; 71 u32 sta_mask; 72 int ctl_offs; 73 u32 sram_pdn_bits; 74 u32 sram_pdn_ack_bits; 75 u32 bus_prot_mask; 76 }; 77 78 struct scp_domain { 79 void __iomem *base; 80 void __iomem *infracfg; 81 enum scp_domain_type type; 82 struct scp_domain_data *data; 83 }; 84 85 static struct scp_domain_data scp_domain_mt7623[] = { 86 [MT7623_POWER_DOMAIN_CONN] = { 87 .sta_mask = PWR_STATUS_CONN, 88 .ctl_offs = SPM_CONN_PWR_CON, 89 .bus_prot_mask = BIT(8) | BIT(2), 90 }, 91 [MT7623_POWER_DOMAIN_DISP] = { 92 .sta_mask = PWR_STATUS_DISP, 93 .ctl_offs = SPM_DIS_PWR_CON, 94 .sram_pdn_bits = GENMASK(11, 8), 95 .bus_prot_mask = BIT(2), 96 }, 97 [MT7623_POWER_DOMAIN_MFG] = { 98 .sta_mask = PWR_STATUS_MFG, 99 .ctl_offs = SPM_MFG_PWR_CON, 100 .sram_pdn_bits = GENMASK(11, 8), 101 .sram_pdn_ack_bits = GENMASK(12, 12), 102 }, 103 [MT7623_POWER_DOMAIN_VDEC] = { 104 .sta_mask = PWR_STATUS_VDEC, 105 .ctl_offs = SPM_VDE_PWR_CON, 106 .sram_pdn_bits = GENMASK(11, 8), 107 .sram_pdn_ack_bits = GENMASK(12, 12), 108 }, 109 [MT7623_POWER_DOMAIN_ISP] = { 110 .sta_mask = PWR_STATUS_ISP, 111 .ctl_offs = SPM_ISP_PWR_CON, 112 .sram_pdn_bits = GENMASK(11, 8), 113 .sram_pdn_ack_bits = GENMASK(13, 12), 114 }, 115 [MT7623_POWER_DOMAIN_BDP] = { 116 .sta_mask = PWR_STATUS_BDP, 117 .ctl_offs = SPM_BDP_PWR_CON, 118 .sram_pdn_bits = GENMASK(11, 8), 119 }, 120 [MT7623_POWER_DOMAIN_ETH] = { 121 .sta_mask = PWR_STATUS_ETH, 122 .ctl_offs = SPM_ETH_PWR_CON, 123 .sram_pdn_bits = GENMASK(11, 8), 124 .sram_pdn_ack_bits = GENMASK(15, 12), 125 }, 126 [MT7623_POWER_DOMAIN_HIF] = { 127 .sta_mask = PWR_STATUS_HIF, 128 .ctl_offs = SPM_HIF_PWR_CON, 129 .sram_pdn_bits = GENMASK(11, 8), 130 .sram_pdn_ack_bits = GENMASK(15, 12), 131 }, 132 [MT7623_POWER_DOMAIN_IFR_MSC] = { 133 .sta_mask = PWR_STATUS_IFR_MSC, 134 .ctl_offs = SPM_IFR_MSC_PWR_CON, 135 }, 136 }; 137 138 static struct scp_domain_data scp_domain_mt7629[] = { 139 [MT7629_POWER_DOMAIN_ETHSYS] = { 140 .sta_mask = PWR_STATUS_ETHSYS, 141 .ctl_offs = SPM_ETHSYS_PWR_CON, 142 .sram_pdn_bits = GENMASK(11, 8), 143 .sram_pdn_ack_bits = GENMASK(15, 12), 144 .bus_prot_mask = (BIT(3) | BIT(17)), 145 }, 146 [MT7629_POWER_DOMAIN_HIF0] = { 147 .sta_mask = PWR_STATUS_HIF0, 148 .ctl_offs = SPM_HIF0_PWR_CON, 149 .sram_pdn_bits = GENMASK(11, 8), 150 .sram_pdn_ack_bits = GENMASK(15, 12), 151 .bus_prot_mask = GENMASK(25, 24), 152 }, 153 [MT7629_POWER_DOMAIN_HIF1] = { 154 .sta_mask = PWR_STATUS_HIF1, 155 .ctl_offs = SPM_HIF1_PWR_CON, 156 .sram_pdn_bits = GENMASK(11, 8), 157 .sram_pdn_ack_bits = GENMASK(15, 12), 158 .bus_prot_mask = GENMASK(28, 26), 159 }, 160 }; 161 162 /** 163 * This function enables the bus protection bits for disabled power 164 * domains so that the system does not hang when some unit accesses the 165 * bus while in power down. 166 */ 167 static int mtk_infracfg_set_bus_protection(void __iomem *infracfg, 168 u32 mask) 169 { 170 u32 val; 171 172 clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask); 173 174 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val, 175 (val & mask) == mask, 100); 176 } 177 178 static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg, 179 u32 mask) 180 { 181 u32 val; 182 183 clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask); 184 185 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val, 186 !(val & mask), 100); 187 } 188 189 static int scpsys_domain_is_on(struct scp_domain_data *data) 190 { 191 struct scp_domain *scpd = data->scpd; 192 u32 sta = readl(scpd->base + SPM_PWR_STATUS) & 193 data->sta_mask; 194 u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) & 195 data->sta_mask; 196 197 /* 198 * A domain is on when both status bits are set. If only one is set 199 * return an error. This happens while powering up a domain 200 */ 201 if (sta && sta2) 202 return true; 203 if (!sta && !sta2) 204 return false; 205 206 return -EINVAL; 207 } 208 209 static int scpsys_power_on(struct power_domain *power_domain) 210 { 211 struct scp_domain *scpd = dev_get_priv(power_domain->dev); 212 struct scp_domain_data *data = &scpd->data[power_domain->id]; 213 void __iomem *ctl_addr = scpd->base + data->ctl_offs; 214 u32 pdn_ack = data->sram_pdn_ack_bits; 215 u32 val; 216 int ret, tmp; 217 218 writel(SPM_EN, scpd->base); 219 220 val = readl(ctl_addr); 221 val |= PWR_ON_BIT; 222 writel(val, ctl_addr); 223 224 val |= PWR_ON_2ND_BIT; 225 writel(val, ctl_addr); 226 227 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0, 228 100); 229 if (ret < 0) 230 return ret; 231 232 val &= ~PWR_CLK_DIS_BIT; 233 writel(val, ctl_addr); 234 235 val &= ~PWR_ISO_BIT; 236 writel(val, ctl_addr); 237 238 val |= PWR_RST_B_BIT; 239 writel(val, ctl_addr); 240 241 val &= ~data->sram_pdn_bits; 242 writel(val, ctl_addr); 243 244 ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100); 245 if (ret < 0) 246 return ret; 247 248 if (data->bus_prot_mask) { 249 ret = mtk_infracfg_clear_bus_protection(scpd->infracfg, 250 data->bus_prot_mask); 251 if (ret) 252 return ret; 253 } 254 255 return 0; 256 } 257 258 static int scpsys_power_off(struct power_domain *power_domain) 259 { 260 struct scp_domain *scpd = dev_get_priv(power_domain->dev); 261 struct scp_domain_data *data = &scpd->data[power_domain->id]; 262 void __iomem *ctl_addr = scpd->base + data->ctl_offs; 263 u32 pdn_ack = data->sram_pdn_ack_bits; 264 u32 val; 265 int ret, tmp; 266 267 if (data->bus_prot_mask) { 268 ret = mtk_infracfg_set_bus_protection(scpd->infracfg, 269 data->bus_prot_mask); 270 if (ret) 271 return ret; 272 } 273 274 val = readl(ctl_addr); 275 val |= data->sram_pdn_bits; 276 writel(val, ctl_addr); 277 278 ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack, 279 100); 280 if (ret < 0) 281 return ret; 282 283 val |= PWR_ISO_BIT; 284 writel(val, ctl_addr); 285 286 val &= ~PWR_RST_B_BIT; 287 writel(val, ctl_addr); 288 289 val |= PWR_CLK_DIS_BIT; 290 writel(val, ctl_addr); 291 292 val &= ~PWR_ON_BIT; 293 writel(val, ctl_addr); 294 295 val &= ~PWR_ON_2ND_BIT; 296 writel(val, ctl_addr); 297 298 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100); 299 if (ret < 0) 300 return ret; 301 302 return 0; 303 } 304 305 static int scpsys_power_request(struct power_domain *power_domain) 306 { 307 struct scp_domain *scpd = dev_get_priv(power_domain->dev); 308 struct scp_domain_data *data; 309 310 data = &scpd->data[power_domain->id]; 311 data->scpd = scpd; 312 313 return 0; 314 } 315 316 static int scpsys_power_free(struct power_domain *power_domain) 317 { 318 return 0; 319 } 320 321 static int mtk_power_domain_hook(struct udevice *dev) 322 { 323 struct scp_domain *scpd = dev_get_priv(dev); 324 325 scpd->type = (enum scp_domain_type)dev_get_driver_data(dev); 326 327 switch (scpd->type) { 328 case SCPSYS_MT7623: 329 scpd->data = scp_domain_mt7623; 330 break; 331 case SCPSYS_MT7629: 332 scpd->data = scp_domain_mt7629; 333 break; 334 default: 335 return -EINVAL; 336 } 337 338 return 0; 339 } 340 341 static int mtk_power_domain_probe(struct udevice *dev) 342 { 343 struct ofnode_phandle_args args; 344 struct scp_domain *scpd = dev_get_priv(dev); 345 struct regmap *regmap; 346 struct clk_bulk bulk; 347 int err; 348 349 scpd->base = dev_read_addr_ptr(dev); 350 if (!scpd->base) 351 return -ENOENT; 352 353 err = mtk_power_domain_hook(dev); 354 if (err) 355 return err; 356 357 /* get corresponding syscon phandle */ 358 err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args); 359 if (err) 360 return err; 361 362 regmap = syscon_node_to_regmap(args.node); 363 if (IS_ERR(regmap)) 364 return PTR_ERR(regmap); 365 366 scpd->infracfg = regmap_get_range(regmap, 0); 367 if (!scpd->infracfg) 368 return -ENOENT; 369 370 /* enable Infra DCM */ 371 setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN); 372 373 err = clk_get_bulk(dev, &bulk); 374 if (err) 375 return err; 376 377 return clk_enable_bulk(&bulk); 378 } 379 380 static const struct udevice_id mtk_power_domain_ids[] = { 381 { 382 .compatible = "mediatek,mt7623-scpsys", 383 .data = SCPSYS_MT7623, 384 }, 385 { 386 .compatible = "mediatek,mt7629-scpsys", 387 .data = SCPSYS_MT7629, 388 }, 389 { /* sentinel */ } 390 }; 391 392 struct power_domain_ops mtk_power_domain_ops = { 393 .free = scpsys_power_free, 394 .off = scpsys_power_off, 395 .on = scpsys_power_on, 396 .request = scpsys_power_request, 397 }; 398 399 U_BOOT_DRIVER(mtk_power_domain) = { 400 .name = "mtk_power_domain", 401 .id = UCLASS_POWER_DOMAIN, 402 .ops = &mtk_power_domain_ops, 403 .probe = mtk_power_domain_probe, 404 .of_match = mtk_power_domain_ids, 405 .priv_auto_alloc_size = sizeof(struct scp_domain), 406 }; 407