1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Texas Instruments power domain driver 4 * 5 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/ 6 * Tero Kristo <t-kristo@ti.com> 7 */ 8 9#include <asm/io.h> 10#include <common.h> 11#include <dm.h> 12#include <errno.h> 13#include <power-domain-uclass.h> 14#include <soc.h> 15#include <k3-dev.h> 16#include <linux/iopoll.h> 17 18#define PSC_PTCMD 0x120 19#define PSC_PTCMD_H 0x124 20#define PSC_PTSTAT 0x128 21#define PSC_PTSTAT_H 0x12C 22#define PSC_PDSTAT 0x200 23#define PSC_PDCTL 0x300 24#define PSC_MDSTAT 0x800 25#define PSC_MDCTL 0xa00 26 27#define PDCTL_STATE_MASK 0x1 28#define PDCTL_STATE_OFF 0x0 29#define PDCTL_STATE_ON 0x1 30 31#define MDSTAT_STATE_MASK 0x3f 32#define MDSTAT_BUSY_MASK 0x30 33#define MDSTAT_STATE_SWRSTDISABLE 0x0 34#define MDSTAT_STATE_ENABLE 0x3 35 36#define LPSC_TIMEOUT 1000 37#define PD_TIMEOUT 1000 38 39static u32 psc_read(struct ti_psc *psc, u32 reg) 40{ 41 u32 val; 42 43 val = readl(psc->base + reg); 44 debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg); 45 return val; 46} 47 48static void psc_write(u32 val, struct ti_psc *psc, u32 reg) 49{ 50 debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg); 51 writel(val, psc->base + reg); 52} 53 54static u32 pd_read(struct ti_pd *pd, u32 reg) 55{ 56 return psc_read(pd->psc, reg + 4 * pd->id); 57} 58 59static void pd_write(u32 val, struct ti_pd *pd, u32 reg) 60{ 61 psc_write(val, pd->psc, reg + 4 * pd->id); 62} 63 64static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg) 65{ 66 return psc_read(lpsc->psc, reg + 4 * lpsc->id); 67} 68 69static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg) 70{ 71 psc_write(val, lpsc->psc, reg + 4 * lpsc->id); 72} 73 74static const struct soc_attr ti_k3_soc_pd_data[] = { 75#if IS_ENABLED(CONFIG_SOC_K3_J721E) 76 { 77 .family = "J721E", 78 .data = &j721e_pd_platdata, 79 }, 80 { 81 .family = "J7200", 82 .data = &j7200_pd_platdata, 83 }, 84#endif 85#if IS_ENABLED(CONFIG_SOC_K3_J721S2) 86 { 87 .family = "J721S2", 88 .data = &j721s2_pd_platdata, 89 }, 90#endif 91#if IS_ENABLED(CONFIG_SOC_K3_AM625) 92 { 93 .family = "AM62X", 94 .data = &am62x_pd_platdata, 95 }, 96#endif 97#if IS_ENABLED(CONFIG_SOC_K3_AM62A7) 98 { 99 .family = "AM62AX", 100 .data = &am62ax_pd_platdata, 101 }, 102#endif 103#if IS_ENABLED(CONFIG_SOC_K3_J784S4) 104 { 105 .family = "J784S4", 106 .data = &j784s4_pd_platdata, 107 }, 108#endif 109#if IS_ENABLED(CONFIG_SOC_K3_AM62P5) 110 { 111 .family = "AM62PX", 112 .data = &am62px_pd_platdata, 113 }, 114#endif 115 { /* sentinel */ } 116}; 117 118static int ti_power_domain_probe(struct udevice *dev) 119{ 120 struct ti_k3_pd_platdata *data = dev_get_priv(dev); 121 const struct soc_attr *soc_match_data; 122 const struct ti_k3_pd_platdata *pdata; 123 124 printf("%s(dev=%p)\n", __func__, dev); 125 126 if (!data) 127 return -ENOMEM; 128 129 soc_match_data = soc_device_match(ti_k3_soc_pd_data); 130 if (!soc_match_data) 131 return -ENODEV; 132 133 pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data; 134 135 data->psc = pdata->psc; 136 data->pd = pdata->pd; 137 data->lpsc = pdata->lpsc; 138 data->devs = pdata->devs; 139 data->num_psc = pdata->num_psc; 140 data->num_pd = pdata->num_pd; 141 data->num_lpsc = pdata->num_lpsc; 142 data->num_devs = pdata->num_devs; 143 144 return 0; 145} 146 147static int ti_pd_wait(struct ti_pd *pd) 148{ 149 u32 ptstat; 150 u32 pdoffset = 0; 151 u32 ptstatreg = PSC_PTSTAT; 152 int ret; 153 154 if (pd->id > 31) { 155 pdoffset = 32; 156 ptstatreg = PSC_PTSTAT_H; 157 } 158 159 ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat, 160 !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT); 161 162 if (ret) 163 printf("%s: psc%d, pd%d failed to transition.\n", __func__, 164 pd->psc->id, pd->id); 165 166 return ret; 167} 168 169static void ti_pd_transition(struct ti_pd *pd) 170{ 171 u32 pdoffset = 0; 172 u32 ptcmdreg = PSC_PTCMD; 173 174 if (pd->id > 31) { 175 pdoffset = 32; 176 ptcmdreg = PSC_PTCMD_H; 177 } 178 179 psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg); 180} 181 182u8 ti_pd_state(struct ti_pd *pd) 183{ 184 return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK; 185} 186 187static int ti_pd_get(struct ti_pd *pd) 188{ 189 u32 pdctl; 190 int ret; 191 192 pd->usecount++; 193 194 if (pd->usecount > 1) 195 return 0; 196 197 if (pd->depend) { 198 ret = ti_pd_get(pd->depend); 199 if (ret) 200 return ret; 201 ti_pd_transition(pd->depend); 202 ret = ti_pd_wait(pd->depend); 203 if (ret) 204 return ret; 205 } 206 207 pdctl = pd_read(pd, PSC_PDCTL); 208 209 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON) 210 return 0; 211 212 debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id); 213 214 pdctl &= ~PDCTL_STATE_MASK; 215 pdctl |= PDCTL_STATE_ON; 216 217 pd_write(pdctl, pd, PSC_PDCTL); 218 219 return 0; 220} 221 222static int ti_pd_put(struct ti_pd *pd) 223{ 224 u32 pdctl; 225 int ret; 226 227 pd->usecount--; 228 229 if (pd->usecount > 0) 230 return 0; 231 232 pdctl = pd_read(pd, PSC_PDCTL); 233 if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF) 234 return 0; 235 236 pdctl &= ~PDCTL_STATE_MASK; 237 pdctl |= PDCTL_STATE_OFF; 238 239 debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id); 240 241 pd_write(pdctl, pd, PSC_PDCTL); 242 243 if (pd->depend) { 244 ti_pd_transition(pd); 245 ret = ti_pd_wait(pd); 246 if (ret) 247 return ret; 248 249 ret = ti_pd_put(pd->depend); 250 if (ret) 251 return ret; 252 ti_pd_transition(pd->depend); 253 ret = ti_pd_wait(pd->depend); 254 if (ret) 255 return ret; 256 } 257 258 return 0; 259} 260 261static int ti_lpsc_wait(struct ti_lpsc *lpsc) 262{ 263 u32 mdstat; 264 int ret; 265 266 ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4, 267 mdstat, 268 !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT); 269 270 if (ret) 271 printf("%s: module %d failed to transition.\n", __func__, 272 lpsc->id); 273 274 return ret; 275} 276 277u8 lpsc_get_state(struct ti_lpsc *lpsc) 278{ 279 return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_STATE_MASK; 280} 281 282int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state) 283{ 284 struct ti_pd *psc_pd; 285 int ret; 286 u32 mdctl; 287 288 psc_pd = lpsc->pd; 289 290 if (state == MDSTAT_STATE_ENABLE) { 291 lpsc->usecount++; 292 if (lpsc->usecount > 1) 293 return 0; 294 } else { 295 lpsc->usecount--; 296 if (lpsc->usecount >= 1) 297 return 0; 298 } 299 300 debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__, 301 lpsc->psc->id, lpsc->id, state); 302 303 if (lpsc->depend) 304 ti_lpsc_transition(lpsc->depend, state); 305 306 mdctl = lpsc_read(lpsc, PSC_MDCTL); 307 if ((mdctl & MDSTAT_STATE_MASK) == state) 308 return 0; 309 310 if (state == MDSTAT_STATE_ENABLE) 311 ti_pd_get(psc_pd); 312 else 313 ti_pd_put(psc_pd); 314 315 mdctl &= ~MDSTAT_STATE_MASK; 316 mdctl |= state; 317 318 lpsc_write(mdctl, lpsc, PSC_MDCTL); 319 320 ti_pd_transition(psc_pd); 321 ret = ti_pd_wait(psc_pd); 322 if (ret) 323 return ret; 324 325 return ti_lpsc_wait(lpsc); 326} 327 328static int ti_power_domain_transition(struct power_domain *pd, u8 state) 329{ 330 struct ti_lpsc *lpsc = pd->priv; 331 332 return ti_lpsc_transition(lpsc, state); 333} 334 335static int ti_power_domain_on(struct power_domain *pd) 336{ 337 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id); 338 339 return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE); 340} 341 342static int ti_power_domain_off(struct power_domain *pd) 343{ 344 debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id); 345 346 return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE); 347} 348 349static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id) 350{ 351 int idx; 352 353 for (idx = 0; idx < data->num_devs; idx++) 354 if (data->devs[idx].id == id) 355 return data->devs[idx].lpsc; 356 357 return NULL; 358} 359 360static int ti_power_domain_of_xlate(struct power_domain *pd, 361 struct ofnode_phandle_args *args) 362{ 363 struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev); 364 struct ti_lpsc *lpsc; 365 366 debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]); 367 368 if (args->args_count < 1) { 369 printf("Invalid args_count: %d\n", args->args_count); 370 return -EINVAL; 371 } 372 373 lpsc = lpsc_lookup(data, args->args[0]); 374 if (!lpsc) { 375 printf("%s: invalid dev-id: %d\n", __func__, args->args[0]); 376 return -ENOENT; 377 } 378 379 pd->id = lpsc->id; 380 pd->priv = lpsc; 381 382 return 0; 383} 384static const struct udevice_id ti_power_domain_of_match[] = { 385 { .compatible = "ti,sci-pm-domain" }, 386 { /* sentinel */ } 387}; 388 389static struct power_domain_ops ti_power_domain_ops = { 390 .on = ti_power_domain_on, 391 .off = ti_power_domain_off, 392 .of_xlate = ti_power_domain_of_xlate, 393}; 394 395U_BOOT_DRIVER(ti_pm_domains) = { 396 .name = "ti-pm-domains", 397 .id = UCLASS_POWER_DOMAIN, 398 .of_match = ti_power_domain_of_match, 399 .probe = ti_power_domain_probe, 400 .priv_auto = sizeof(struct ti_k3_pd_platdata), 401 .ops = &ti_power_domain_ops, 402}; 403