992 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * OMAP2+ PRM driver4 *5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/6 * Tero Kristo <t-kristo@ti.com>7 */8 9#include <linux/kernel.h>10#include <linux/clk.h>11#include <linux/device.h>12#include <linux/io.h>13#include <linux/iopoll.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/pm_clock.h>18#include <linux/pm_domain.h>19#include <linux/reset-controller.h>20#include <linux/delay.h>21 22#include <linux/platform_data/ti-prm.h>23 24enum omap_prm_domain_mode {25 OMAP_PRMD_OFF,26 OMAP_PRMD_RETENTION,27 OMAP_PRMD_ON_INACTIVE,28 OMAP_PRMD_ON_ACTIVE,29};30 31struct omap_prm_domain_map {32 unsigned int usable_modes; /* Mask of hardware supported modes */33 unsigned long statechange:1; /* Optional low-power state change */34 unsigned long logicretstate:1; /* Optional logic off mode */35};36 37struct omap_prm_domain {38 struct device *dev;39 struct omap_prm *prm;40 struct generic_pm_domain pd;41 u16 pwrstctrl;42 u16 pwrstst;43 const struct omap_prm_domain_map *cap;44 u32 pwrstctrl_saved;45 unsigned int uses_pm_clk:1;46};47 48struct omap_rst_map {49 s8 rst;50 s8 st;51};52 53struct omap_prm_data {54 u32 base;55 const char *name;56 const char *clkdm_name;57 u16 pwrstctrl;58 u16 pwrstst;59 const struct omap_prm_domain_map *dmap;60 u16 rstctrl;61 u16 rstst;62 const struct omap_rst_map *rstmap;63 u8 flags;64};65 66struct omap_prm {67 const struct omap_prm_data *data;68 void __iomem *base;69 struct omap_prm_domain *prmd;70};71 72struct omap_reset_data {73 struct reset_controller_dev rcdev;74 struct omap_prm *prm;75 u32 mask;76 spinlock_t lock;77 struct clockdomain *clkdm;78 struct device *dev;79};80 81#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd)82#define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev)83 84#define OMAP_MAX_RESETS 885#define OMAP_RESET_MAX_WAIT 1000086 87#define OMAP_PRM_HAS_RSTCTRL BIT(0)88#define OMAP_PRM_HAS_RSTST BIT(1)89#define OMAP_PRM_HAS_NO_CLKDM BIT(2)90#define OMAP_PRM_RET_WHEN_IDLE BIT(3)91 92#define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST)93 94#define PRM_STATE_MAX_WAIT 1000095#define PRM_LOGICRETSTATE BIT(2)96#define PRM_LOWPOWERSTATECHANGE BIT(4)97#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE98 99#define PRM_ST_INTRANSITION BIT(20)100 101static const struct omap_prm_domain_map omap_prm_all = {102 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) |103 BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF),104 .statechange = 1,105 .logicretstate = 1,106};107 108static const struct omap_prm_domain_map omap_prm_noinact = {109 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) |110 BIT(OMAP_PRMD_OFF),111 .statechange = 1,112 .logicretstate = 1,113};114 115static const struct omap_prm_domain_map omap_prm_nooff = {116 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) |117 BIT(OMAP_PRMD_RETENTION),118 .statechange = 1,119 .logicretstate = 1,120};121 122static const struct omap_prm_domain_map omap_prm_onoff_noauto = {123 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF),124 .statechange = 1,125};126 127static const struct omap_prm_domain_map omap_prm_alwon = {128 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE),129};130 131static const struct omap_prm_domain_map omap_prm_reton = {132 .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION),133 .statechange = 1,134 .logicretstate = 1,135};136 137static const struct omap_rst_map rst_map_0[] = {138 { .rst = 0, .st = 0 },139 { .rst = -1 },140};141 142static const struct omap_rst_map rst_map_01[] = {143 { .rst = 0, .st = 0 },144 { .rst = 1, .st = 1 },145 { .rst = -1 },146};147 148static const struct omap_rst_map rst_map_012[] = {149 { .rst = 0, .st = 0 },150 { .rst = 1, .st = 1 },151 { .rst = 2, .st = 2 },152 { .rst = -1 },153};154 155static const struct omap_prm_data omap4_prm_data[] = {156 {157 .name = "mpu", .base = 0x4a306300,158 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,159 },160 {161 .name = "tesla", .base = 0x4a306400,162 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,163 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01164 },165 {166 .name = "abe", .base = 0x4a306500,167 .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_all,168 },169 {170 .name = "always_on_core", .base = 0x4a306600,171 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,172 },173 {174 .name = "core", .base = 0x4a306700,175 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,176 .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati",177 .rstmap = rst_map_012,178 .flags = OMAP_PRM_RET_WHEN_IDLE,179 },180 {181 .name = "ivahd", .base = 0x4a306f00,182 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,183 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012184 },185 {186 .name = "cam", .base = 0x4a307000,187 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,188 },189 {190 .name = "dss", .base = 0x4a307100,191 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact192 },193 {194 .name = "gfx", .base = 0x4a307200,195 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto196 },197 {198 .name = "l3init", .base = 0x4a307300,199 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton200 },201 {202 .name = "l4per", .base = 0x4a307400,203 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,204 .flags = OMAP_PRM_RET_WHEN_IDLE,205 },206 {207 .name = "cefuse", .base = 0x4a307600,208 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto209 },210 {211 .name = "wkup", .base = 0x4a307700,212 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon213 },214 {215 .name = "emu", .base = 0x4a307900,216 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto217 },218 {219 .name = "device", .base = 0x4a307b00,220 .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01,221 .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM222 },223 { },224};225 226static const struct omap_prm_data omap5_prm_data[] = {227 {228 .name = "mpu", .base = 0x4ae06300,229 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,230 },231 {232 .name = "dsp", .base = 0x4ae06400,233 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,234 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01235 },236 {237 .name = "abe", .base = 0x4ae06500,238 .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_nooff,239 },240 {241 .name = "coreaon", .base = 0x4ae06600,242 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon243 },244 {245 .name = "core", .base = 0x4ae06700,246 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,247 .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu",248 .rstmap = rst_map_012249 },250 {251 .name = "iva", .base = 0x4ae07200,252 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,253 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012254 },255 {256 .name = "cam", .base = 0x4ae07300,257 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto258 },259 {260 .name = "dss", .base = 0x4ae07400,261 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact262 },263 {264 .name = "gpu", .base = 0x4ae07500,265 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto266 },267 {268 .name = "l3init", .base = 0x4ae07600,269 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton270 },271 {272 .name = "custefuse", .base = 0x4ae07700,273 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto274 },275 {276 .name = "wkupaon", .base = 0x4ae07800,277 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon278 },279 {280 .name = "emu", .base = 0x4ae07a00,281 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto282 },283 {284 .name = "device", .base = 0x4ae07c00,285 .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01,286 .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM287 },288 { },289};290 291static const struct omap_prm_data dra7_prm_data[] = {292 {293 .name = "mpu", .base = 0x4ae06300,294 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton,295 },296 {297 .name = "dsp1", .base = 0x4ae06400,298 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,299 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01,300 },301 {302 .name = "ipu", .base = 0x4ae06500,303 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,304 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012,305 .clkdm_name = "ipu1"306 },307 {308 .name = "coreaon", .base = 0x4ae06628,309 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,310 },311 {312 .name = "core", .base = 0x4ae06700,313 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,314 .rstctrl = 0x210, .rstst = 0x214, .rstmap = rst_map_012,315 .clkdm_name = "ipu2"316 },317 {318 .name = "iva", .base = 0x4ae06f00,319 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,320 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012,321 },322 {323 .name = "cam", .base = 0x4ae07000,324 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,325 },326 {327 .name = "dss", .base = 0x4ae07100,328 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,329 },330 {331 .name = "gpu", .base = 0x4ae07200,332 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,333 },334 {335 .name = "l3init", .base = 0x4ae07300,336 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,337 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01,338 .clkdm_name = "pcie"339 },340 {341 .name = "l4per", .base = 0x4ae07400,342 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,343 },344 {345 .name = "custefuse", .base = 0x4ae07600,346 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,347 },348 {349 .name = "wkupaon", .base = 0x4ae07724,350 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,351 },352 {353 .name = "emu", .base = 0x4ae07900,354 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,355 },356 {357 .name = "dsp2", .base = 0x4ae07b00,358 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,359 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01360 },361 {362 .name = "eve1", .base = 0x4ae07b40,363 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,364 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01365 },366 {367 .name = "eve2", .base = 0x4ae07b80,368 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,369 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01370 },371 {372 .name = "eve3", .base = 0x4ae07bc0,373 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,374 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01375 },376 {377 .name = "eve4", .base = 0x4ae07c00,378 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,379 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01380 },381 {382 .name = "rtc", .base = 0x4ae07c60,383 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,384 },385 {386 .name = "vpe", .base = 0x4ae07c80,387 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,388 },389 { },390};391 392static const struct omap_rst_map am3_per_rst_map[] = {393 { .rst = 1 },394 { .rst = -1 },395};396 397static const struct omap_rst_map am3_wkup_rst_map[] = {398 { .rst = 3, .st = 5 },399 { .rst = -1 },400};401 402static const struct omap_prm_data am3_prm_data[] = {403 {404 .name = "per", .base = 0x44e00c00,405 .pwrstctrl = 0xc, .pwrstst = 0x8, .dmap = &omap_prm_noinact,406 .rstctrl = 0x0, .rstmap = am3_per_rst_map,407 .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp"408 },409 {410 .name = "wkup", .base = 0x44e00d00,411 .pwrstctrl = 0x4, .pwrstst = 0x4, .dmap = &omap_prm_alwon,412 .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map,413 .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM414 },415 {416 .name = "mpu", .base = 0x44e00e00,417 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,418 },419 {420 .name = "device", .base = 0x44e00f00,421 .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01,422 .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM423 },424 {425 .name = "rtc", .base = 0x44e01000,426 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,427 },428 {429 .name = "gfx", .base = 0x44e01100,430 .pwrstctrl = 0, .pwrstst = 0x10, .dmap = &omap_prm_noinact,431 .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3",432 },433 {434 .name = "cefuse", .base = 0x44e01200,435 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,436 },437 { },438};439 440static const struct omap_rst_map am4_per_rst_map[] = {441 { .rst = 1, .st = 0 },442 { .rst = -1 },443};444 445static const struct omap_rst_map am4_device_rst_map[] = {446 { .rst = 0, .st = 1 },447 { .rst = 1, .st = 0 },448 { .rst = -1 },449};450 451static const struct omap_prm_data am4_prm_data[] = {452 {453 .name = "mpu", .base = 0x44df0300,454 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,455 },456 {457 .name = "gfx", .base = 0x44df0400,458 .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,459 .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3",460 },461 {462 .name = "rtc", .base = 0x44df0500,463 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,464 },465 {466 .name = "tamper", .base = 0x44df0600,467 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,468 },469 {470 .name = "cefuse", .base = 0x44df0700,471 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto,472 },473 {474 .name = "per", .base = 0x44df0800,475 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact,476 .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map,477 .clkdm_name = "pruss_ocp"478 },479 {480 .name = "wkup", .base = 0x44df2000,481 .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon,482 .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map,483 .flags = OMAP_PRM_HAS_NO_CLKDM484 },485 {486 .name = "device", .base = 0x44df4000,487 .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map,488 .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM489 },490 { },491};492 493static const struct of_device_id omap_prm_id_table[] = {494 { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data },495 { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data },496 { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data },497 { .compatible = "ti,am3-prm-inst", .data = am3_prm_data },498 { .compatible = "ti,am4-prm-inst", .data = am4_prm_data },499 { },500};501 502#ifdef DEBUG503static void omap_prm_domain_show_state(struct omap_prm_domain *prmd,504 const char *desc)505{506 dev_dbg(prmd->dev, "%s %s: %08x/%08x\n",507 prmd->pd.name, desc,508 readl_relaxed(prmd->prm->base + prmd->pwrstctrl),509 readl_relaxed(prmd->prm->base + prmd->pwrstst));510}511#else512static inline void omap_prm_domain_show_state(struct omap_prm_domain *prmd,513 const char *desc)514{515}516#endif517 518static int omap_prm_domain_power_on(struct generic_pm_domain *domain)519{520 struct omap_prm_domain *prmd;521 int ret;522 u32 v, mode;523 524 prmd = genpd_to_prm_domain(domain);525 if (!prmd->cap)526 return 0;527 528 omap_prm_domain_show_state(prmd, "on: previous state");529 530 if (prmd->pwrstctrl_saved)531 v = prmd->pwrstctrl_saved;532 else533 v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl);534 535 if (prmd->prm->data->flags & OMAP_PRM_RET_WHEN_IDLE)536 mode = OMAP_PRMD_RETENTION;537 else538 mode = OMAP_PRMD_ON_ACTIVE;539 540 writel_relaxed((v & ~PRM_POWERSTATE_MASK) | mode,541 prmd->prm->base + prmd->pwrstctrl);542 543 /* wait for the transition bit to get cleared */544 ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst,545 v, !(v & PRM_ST_INTRANSITION), 1,546 PRM_STATE_MAX_WAIT);547 if (ret)548 dev_err(prmd->dev, "%s: %s timed out\n",549 prmd->pd.name, __func__);550 551 omap_prm_domain_show_state(prmd, "on: new state");552 553 return ret;554}555 556/* No need to check for holes in the mask for the lowest mode */557static int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd)558{559 return __ffs(prmd->cap->usable_modes);560}561 562static int omap_prm_domain_power_off(struct generic_pm_domain *domain)563{564 struct omap_prm_domain *prmd;565 int ret;566 u32 v;567 568 prmd = genpd_to_prm_domain(domain);569 if (!prmd->cap)570 return 0;571 572 omap_prm_domain_show_state(prmd, "off: previous state");573 574 v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl);575 prmd->pwrstctrl_saved = v;576 577 v &= ~PRM_POWERSTATE_MASK;578 v |= omap_prm_domain_find_lowest(prmd);579 580 if (prmd->cap->statechange)581 v |= PRM_LOWPOWERSTATECHANGE;582 if (prmd->cap->logicretstate)583 v &= ~PRM_LOGICRETSTATE;584 else585 v |= PRM_LOGICRETSTATE;586 587 writel_relaxed(v, prmd->prm->base + prmd->pwrstctrl);588 589 /* wait for the transition bit to get cleared */590 ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst,591 v, !(v & PRM_ST_INTRANSITION), 1,592 PRM_STATE_MAX_WAIT);593 if (ret)594 dev_warn(prmd->dev, "%s: %s timed out\n",595 __func__, prmd->pd.name);596 597 omap_prm_domain_show_state(prmd, "off: new state");598 599 return 0;600}601 602/*603 * Note that ti-sysc already manages the module clocks separately so604 * no need to manage those. Interconnect instances need clocks managed605 * for simple-pm-bus.606 */607static int omap_prm_domain_attach_clock(struct device *dev,608 struct omap_prm_domain *prmd)609{610 struct device_node *np = dev->of_node;611 int error;612 613 if (!of_device_is_compatible(np, "simple-pm-bus"))614 return 0;615 616 if (!of_property_read_bool(np, "clocks"))617 return 0;618 619 error = pm_clk_create(dev);620 if (error)621 return error;622 623 error = of_pm_clk_add_clks(dev);624 if (error < 0) {625 pm_clk_destroy(dev);626 return error;627 }628 629 prmd->uses_pm_clk = 1;630 631 return 0;632}633 634static int omap_prm_domain_attach_dev(struct generic_pm_domain *domain,635 struct device *dev)636{637 struct generic_pm_domain_data *genpd_data;638 struct of_phandle_args pd_args;639 struct omap_prm_domain *prmd;640 struct device_node *np;641 int ret;642 643 prmd = genpd_to_prm_domain(domain);644 np = dev->of_node;645 646 ret = of_parse_phandle_with_args(np, "power-domains",647 "#power-domain-cells", 0, &pd_args);648 if (ret < 0)649 return ret;650 651 if (pd_args.args_count != 0)652 dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n",653 prmd->pd.name, pd_args.args_count);654 655 genpd_data = dev_gpd_data(dev);656 genpd_data->data = NULL;657 658 ret = omap_prm_domain_attach_clock(dev, prmd);659 if (ret)660 return ret;661 662 return 0;663}664 665static void omap_prm_domain_detach_dev(struct generic_pm_domain *domain,666 struct device *dev)667{668 struct generic_pm_domain_data *genpd_data;669 struct omap_prm_domain *prmd;670 671 prmd = genpd_to_prm_domain(domain);672 if (prmd->uses_pm_clk)673 pm_clk_destroy(dev);674 genpd_data = dev_gpd_data(dev);675 genpd_data->data = NULL;676}677 678static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm)679{680 struct omap_prm_domain *prmd;681 struct device_node *np = dev->of_node;682 const struct omap_prm_data *data;683 const char *name;684 int error;685 686 if (!of_property_present(dev->of_node, "#power-domain-cells"))687 return 0;688 689 of_node_put(dev->of_node);690 691 prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL);692 if (!prmd)693 return -ENOMEM;694 695 data = prm->data;696 name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s",697 data->name);698 if (!name)699 return -ENOMEM;700 701 prmd->dev = dev;702 prmd->prm = prm;703 prmd->cap = prmd->prm->data->dmap;704 prmd->pwrstctrl = prmd->prm->data->pwrstctrl;705 prmd->pwrstst = prmd->prm->data->pwrstst;706 707 prmd->pd.name = name;708 prmd->pd.power_on = omap_prm_domain_power_on;709 prmd->pd.power_off = omap_prm_domain_power_off;710 prmd->pd.attach_dev = omap_prm_domain_attach_dev;711 prmd->pd.detach_dev = omap_prm_domain_detach_dev;712 prmd->pd.flags = GENPD_FLAG_PM_CLK;713 714 pm_genpd_init(&prmd->pd, NULL, true);715 error = of_genpd_add_provider_simple(np, &prmd->pd);716 if (error)717 pm_genpd_remove(&prmd->pd);718 else719 prm->prmd = prmd;720 721 return error;722}723 724static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id)725{726 if (reset->mask & BIT(id))727 return true;728 729 return false;730}731 732static int omap_reset_get_st_bit(struct omap_reset_data *reset,733 unsigned long id)734{735 const struct omap_rst_map *map = reset->prm->data->rstmap;736 737 while (map->rst >= 0) {738 if (map->rst == id)739 return map->st;740 741 map++;742 }743 744 return id;745}746 747static int omap_reset_status(struct reset_controller_dev *rcdev,748 unsigned long id)749{750 struct omap_reset_data *reset = to_omap_reset_data(rcdev);751 u32 v;752 int st_bit = omap_reset_get_st_bit(reset, id);753 bool has_rstst = reset->prm->data->rstst ||754 (reset->prm->data->flags & OMAP_PRM_HAS_RSTST);755 756 /* Check if we have rstst */757 if (!has_rstst)758 return -ENOTSUPP;759 760 /* Check if hw reset line is asserted */761 v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl);762 if (v & BIT(id))763 return 1;764 765 /*766 * Check reset status, high value means reset sequence has been767 * completed successfully so we can return 0 here (reset deasserted)768 */769 v = readl_relaxed(reset->prm->base + reset->prm->data->rstst);770 v >>= st_bit;771 v &= 1;772 773 return !v;774}775 776static int omap_reset_assert(struct reset_controller_dev *rcdev,777 unsigned long id)778{779 struct omap_reset_data *reset = to_omap_reset_data(rcdev);780 u32 v;781 unsigned long flags;782 783 /* assert the reset control line */784 spin_lock_irqsave(&reset->lock, flags);785 v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl);786 v |= 1 << id;787 writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl);788 spin_unlock_irqrestore(&reset->lock, flags);789 790 return 0;791}792 793static int omap_reset_deassert(struct reset_controller_dev *rcdev,794 unsigned long id)795{796 struct omap_reset_data *reset = to_omap_reset_data(rcdev);797 u32 v;798 int st_bit;799 bool has_rstst;800 unsigned long flags;801 struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev);802 int ret = 0;803 804 /* Nothing to do if the reset is already deasserted */805 if (!omap_reset_status(rcdev, id))806 return 0;807 808 has_rstst = reset->prm->data->rstst ||809 (reset->prm->data->flags & OMAP_PRM_HAS_RSTST);810 811 if (has_rstst) {812 st_bit = omap_reset_get_st_bit(reset, id);813 814 /* Clear the reset status by writing 1 to the status bit */815 v = 1 << st_bit;816 writel_relaxed(v, reset->prm->base + reset->prm->data->rstst);817 }818 819 if (reset->clkdm)820 pdata->clkdm_deny_idle(reset->clkdm);821 822 /* de-assert the reset control line */823 spin_lock_irqsave(&reset->lock, flags);824 v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl);825 v &= ~(1 << id);826 writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl);827 spin_unlock_irqrestore(&reset->lock, flags);828 829 /* wait for the reset bit to clear */830 ret = readl_relaxed_poll_timeout_atomic(reset->prm->base +831 reset->prm->data->rstctrl,832 v, !(v & BIT(id)), 1,833 OMAP_RESET_MAX_WAIT);834 if (ret)835 pr_err("%s: timedout waiting for %s:%lu\n", __func__,836 reset->prm->data->name, id);837 838 /* wait for the status to be set */839 if (has_rstst) {840 ret = readl_relaxed_poll_timeout_atomic(reset->prm->base +841 reset->prm->data->rstst,842 v, v & BIT(st_bit), 1,843 OMAP_RESET_MAX_WAIT);844 if (ret)845 pr_err("%s: timedout waiting for %s:%lu\n", __func__,846 reset->prm->data->name, id);847 }848 849 if (reset->clkdm)850 pdata->clkdm_allow_idle(reset->clkdm);851 852 return ret;853}854 855static const struct reset_control_ops omap_reset_ops = {856 .assert = omap_reset_assert,857 .deassert = omap_reset_deassert,858 .status = omap_reset_status,859};860 861static int omap_prm_reset_xlate(struct reset_controller_dev *rcdev,862 const struct of_phandle_args *reset_spec)863{864 struct omap_reset_data *reset = to_omap_reset_data(rcdev);865 866 if (!_is_valid_reset(reset, reset_spec->args[0]))867 return -EINVAL;868 869 return reset_spec->args[0];870}871 872static int omap_prm_reset_init(struct platform_device *pdev,873 struct omap_prm *prm)874{875 struct omap_reset_data *reset;876 const struct omap_rst_map *map;877 struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev);878 char buf[32];879 u32 v;880 881 /*882 * Check if we have controllable resets. If either rstctrl is non-zero883 * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register884 * for the domain.885 */886 if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL))887 return 0;888 889 /* Check if we have the pdata callbacks in place */890 if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle ||891 !pdata->clkdm_allow_idle)892 return -EINVAL;893 894 map = prm->data->rstmap;895 if (!map)896 return -EINVAL;897 898 reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL);899 if (!reset)900 return -ENOMEM;901 902 reset->rcdev.owner = THIS_MODULE;903 reset->rcdev.ops = &omap_reset_ops;904 reset->rcdev.of_node = pdev->dev.of_node;905 reset->rcdev.nr_resets = OMAP_MAX_RESETS;906 reset->rcdev.of_xlate = omap_prm_reset_xlate;907 reset->rcdev.of_reset_n_cells = 1;908 reset->dev = &pdev->dev;909 spin_lock_init(&reset->lock);910 911 reset->prm = prm;912 913 sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name :914 prm->data->name);915 916 if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) {917 reset->clkdm = pdata->clkdm_lookup(buf);918 if (!reset->clkdm)919 return -EINVAL;920 }921 922 while (map->rst >= 0) {923 reset->mask |= BIT(map->rst);924 map++;925 }926 927 /* Quirk handling to assert rst_map_012 bits on reset and avoid errors */928 if (prm->data->rstmap == rst_map_012) {929 v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl);930 if ((v & reset->mask) != reset->mask) {931 dev_dbg(&pdev->dev, "Asserting all resets: %08x\n", v);932 writel_relaxed(reset->mask, reset->prm->base +933 reset->prm->data->rstctrl);934 }935 }936 937 return devm_reset_controller_register(&pdev->dev, &reset->rcdev);938}939 940static int omap_prm_probe(struct platform_device *pdev)941{942 struct resource *res;943 const struct omap_prm_data *data;944 struct omap_prm *prm;945 int ret;946 947 data = of_device_get_match_data(&pdev->dev);948 if (!data)949 return -ENOTSUPP;950 951 prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL);952 if (!prm)953 return -ENOMEM;954 955 prm->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);956 if (IS_ERR(prm->base))957 return PTR_ERR(prm->base);958 959 while (data->base != res->start) {960 if (!data->base)961 return -EINVAL;962 data++;963 }964 965 prm->data = data;966 967 ret = omap_prm_domain_init(&pdev->dev, prm);968 if (ret)969 return ret;970 971 ret = omap_prm_reset_init(pdev, prm);972 if (ret)973 goto err_domain;974 975 return 0;976 977err_domain:978 of_genpd_del_provider(pdev->dev.of_node);979 pm_genpd_remove(&prm->prmd->pd);980 981 return ret;982}983 984static struct platform_driver omap_prm_driver = {985 .probe = omap_prm_probe,986 .driver = {987 .name = KBUILD_MODNAME,988 .of_match_table = omap_prm_id_table,989 },990};991builtin_platform_driver(omap_prm_driver);992