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