162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP2+ PRM driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ 662306a36Sopenharmony_ci * Tero Kristo <t-kristo@ti.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_clock.h> 1862306a36Sopenharmony_ci#include <linux/pm_domain.h> 1962306a36Sopenharmony_ci#include <linux/reset-controller.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/platform_data/ti-prm.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cienum omap_prm_domain_mode { 2562306a36Sopenharmony_ci OMAP_PRMD_OFF, 2662306a36Sopenharmony_ci OMAP_PRMD_RETENTION, 2762306a36Sopenharmony_ci OMAP_PRMD_ON_INACTIVE, 2862306a36Sopenharmony_ci OMAP_PRMD_ON_ACTIVE, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct omap_prm_domain_map { 3262306a36Sopenharmony_ci unsigned int usable_modes; /* Mask of hardware supported modes */ 3362306a36Sopenharmony_ci unsigned long statechange:1; /* Optional low-power state change */ 3462306a36Sopenharmony_ci unsigned long logicretstate:1; /* Optional logic off mode */ 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct omap_prm_domain { 3862306a36Sopenharmony_ci struct device *dev; 3962306a36Sopenharmony_ci struct omap_prm *prm; 4062306a36Sopenharmony_ci struct generic_pm_domain pd; 4162306a36Sopenharmony_ci u16 pwrstctrl; 4262306a36Sopenharmony_ci u16 pwrstst; 4362306a36Sopenharmony_ci const struct omap_prm_domain_map *cap; 4462306a36Sopenharmony_ci u32 pwrstctrl_saved; 4562306a36Sopenharmony_ci unsigned int uses_pm_clk:1; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct omap_rst_map { 4962306a36Sopenharmony_ci s8 rst; 5062306a36Sopenharmony_ci s8 st; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct omap_prm_data { 5462306a36Sopenharmony_ci u32 base; 5562306a36Sopenharmony_ci const char *name; 5662306a36Sopenharmony_ci const char *clkdm_name; 5762306a36Sopenharmony_ci u16 pwrstctrl; 5862306a36Sopenharmony_ci u16 pwrstst; 5962306a36Sopenharmony_ci const struct omap_prm_domain_map *dmap; 6062306a36Sopenharmony_ci u16 rstctrl; 6162306a36Sopenharmony_ci u16 rstst; 6262306a36Sopenharmony_ci const struct omap_rst_map *rstmap; 6362306a36Sopenharmony_ci u8 flags; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct omap_prm { 6762306a36Sopenharmony_ci const struct omap_prm_data *data; 6862306a36Sopenharmony_ci void __iomem *base; 6962306a36Sopenharmony_ci struct omap_prm_domain *prmd; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct omap_reset_data { 7362306a36Sopenharmony_ci struct reset_controller_dev rcdev; 7462306a36Sopenharmony_ci struct omap_prm *prm; 7562306a36Sopenharmony_ci u32 mask; 7662306a36Sopenharmony_ci spinlock_t lock; 7762306a36Sopenharmony_ci struct clockdomain *clkdm; 7862306a36Sopenharmony_ci struct device *dev; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd) 8262306a36Sopenharmony_ci#define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define OMAP_MAX_RESETS 8 8562306a36Sopenharmony_ci#define OMAP_RESET_MAX_WAIT 10000 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define OMAP_PRM_HAS_RSTCTRL BIT(0) 8862306a36Sopenharmony_ci#define OMAP_PRM_HAS_RSTST BIT(1) 8962306a36Sopenharmony_ci#define OMAP_PRM_HAS_NO_CLKDM BIT(2) 9062306a36Sopenharmony_ci#define OMAP_PRM_RET_WHEN_IDLE BIT(3) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define PRM_STATE_MAX_WAIT 10000 9562306a36Sopenharmony_ci#define PRM_LOGICRETSTATE BIT(2) 9662306a36Sopenharmony_ci#define PRM_LOWPOWERSTATECHANGE BIT(4) 9762306a36Sopenharmony_ci#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define PRM_ST_INTRANSITION BIT(20) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_all = { 10262306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | 10362306a36Sopenharmony_ci BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF), 10462306a36Sopenharmony_ci .statechange = 1, 10562306a36Sopenharmony_ci .logicretstate = 1, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_noinact = { 10962306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) | 11062306a36Sopenharmony_ci BIT(OMAP_PRMD_OFF), 11162306a36Sopenharmony_ci .statechange = 1, 11262306a36Sopenharmony_ci .logicretstate = 1, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_nooff = { 11662306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | 11762306a36Sopenharmony_ci BIT(OMAP_PRMD_RETENTION), 11862306a36Sopenharmony_ci .statechange = 1, 11962306a36Sopenharmony_ci .logicretstate = 1, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_onoff_noauto = { 12362306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF), 12462306a36Sopenharmony_ci .statechange = 1, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_alwon = { 12862306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE), 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct omap_prm_domain_map omap_prm_reton = { 13262306a36Sopenharmony_ci .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION), 13362306a36Sopenharmony_ci .statechange = 1, 13462306a36Sopenharmony_ci .logicretstate = 1, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const struct omap_rst_map rst_map_0[] = { 13862306a36Sopenharmony_ci { .rst = 0, .st = 0 }, 13962306a36Sopenharmony_ci { .rst = -1 }, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const struct omap_rst_map rst_map_01[] = { 14362306a36Sopenharmony_ci { .rst = 0, .st = 0 }, 14462306a36Sopenharmony_ci { .rst = 1, .st = 1 }, 14562306a36Sopenharmony_ci { .rst = -1 }, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic const struct omap_rst_map rst_map_012[] = { 14962306a36Sopenharmony_ci { .rst = 0, .st = 0 }, 15062306a36Sopenharmony_ci { .rst = 1, .st = 1 }, 15162306a36Sopenharmony_ci { .rst = 2, .st = 2 }, 15262306a36Sopenharmony_ci { .rst = -1 }, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct omap_prm_data omap4_prm_data[] = { 15662306a36Sopenharmony_ci { 15762306a36Sopenharmony_ci .name = "mpu", .base = 0x4a306300, 15862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 15962306a36Sopenharmony_ci }, 16062306a36Sopenharmony_ci { 16162306a36Sopenharmony_ci .name = "tesla", .base = 0x4a306400, 16262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 16362306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci { 16662306a36Sopenharmony_ci .name = "abe", .base = 0x4a306500, 16762306a36Sopenharmony_ci .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_all, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci { 17062306a36Sopenharmony_ci .name = "always_on_core", .base = 0x4a306600, 17162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 17262306a36Sopenharmony_ci }, 17362306a36Sopenharmony_ci { 17462306a36Sopenharmony_ci .name = "core", .base = 0x4a306700, 17562306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 17662306a36Sopenharmony_ci .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", 17762306a36Sopenharmony_ci .rstmap = rst_map_012, 17862306a36Sopenharmony_ci .flags = OMAP_PRM_RET_WHEN_IDLE, 17962306a36Sopenharmony_ci }, 18062306a36Sopenharmony_ci { 18162306a36Sopenharmony_ci .name = "ivahd", .base = 0x4a306f00, 18262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 18362306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 18462306a36Sopenharmony_ci }, 18562306a36Sopenharmony_ci { 18662306a36Sopenharmony_ci .name = "cam", .base = 0x4a307000, 18762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 18862306a36Sopenharmony_ci }, 18962306a36Sopenharmony_ci { 19062306a36Sopenharmony_ci .name = "dss", .base = 0x4a307100, 19162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact 19262306a36Sopenharmony_ci }, 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci .name = "gfx", .base = 0x4a307200, 19562306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci { 19862306a36Sopenharmony_ci .name = "l3init", .base = 0x4a307300, 19962306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton 20062306a36Sopenharmony_ci }, 20162306a36Sopenharmony_ci { 20262306a36Sopenharmony_ci .name = "l4per", .base = 0x4a307400, 20362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 20462306a36Sopenharmony_ci .flags = OMAP_PRM_RET_WHEN_IDLE, 20562306a36Sopenharmony_ci }, 20662306a36Sopenharmony_ci { 20762306a36Sopenharmony_ci .name = "cefuse", .base = 0x4a307600, 20862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci { 21162306a36Sopenharmony_ci .name = "wkup", .base = 0x4a307700, 21262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci { 21562306a36Sopenharmony_ci .name = "emu", .base = 0x4a307900, 21662306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci { 21962306a36Sopenharmony_ci .name = "device", .base = 0x4a307b00, 22062306a36Sopenharmony_ci .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, 22162306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM 22262306a36Sopenharmony_ci }, 22362306a36Sopenharmony_ci { }, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct omap_prm_data omap5_prm_data[] = { 22762306a36Sopenharmony_ci { 22862306a36Sopenharmony_ci .name = "mpu", .base = 0x4ae06300, 22962306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 23062306a36Sopenharmony_ci }, 23162306a36Sopenharmony_ci { 23262306a36Sopenharmony_ci .name = "dsp", .base = 0x4ae06400, 23362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 23462306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 23562306a36Sopenharmony_ci }, 23662306a36Sopenharmony_ci { 23762306a36Sopenharmony_ci .name = "abe", .base = 0x4ae06500, 23862306a36Sopenharmony_ci .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_nooff, 23962306a36Sopenharmony_ci }, 24062306a36Sopenharmony_ci { 24162306a36Sopenharmony_ci .name = "coreaon", .base = 0x4ae06600, 24262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon 24362306a36Sopenharmony_ci }, 24462306a36Sopenharmony_ci { 24562306a36Sopenharmony_ci .name = "core", .base = 0x4ae06700, 24662306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 24762306a36Sopenharmony_ci .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", 24862306a36Sopenharmony_ci .rstmap = rst_map_012 24962306a36Sopenharmony_ci }, 25062306a36Sopenharmony_ci { 25162306a36Sopenharmony_ci .name = "iva", .base = 0x4ae07200, 25262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 25362306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 25462306a36Sopenharmony_ci }, 25562306a36Sopenharmony_ci { 25662306a36Sopenharmony_ci .name = "cam", .base = 0x4ae07300, 25762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 25862306a36Sopenharmony_ci }, 25962306a36Sopenharmony_ci { 26062306a36Sopenharmony_ci .name = "dss", .base = 0x4ae07400, 26162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact 26262306a36Sopenharmony_ci }, 26362306a36Sopenharmony_ci { 26462306a36Sopenharmony_ci .name = "gpu", .base = 0x4ae07500, 26562306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 26662306a36Sopenharmony_ci }, 26762306a36Sopenharmony_ci { 26862306a36Sopenharmony_ci .name = "l3init", .base = 0x4ae07600, 26962306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton 27062306a36Sopenharmony_ci }, 27162306a36Sopenharmony_ci { 27262306a36Sopenharmony_ci .name = "custefuse", .base = 0x4ae07700, 27362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 27462306a36Sopenharmony_ci }, 27562306a36Sopenharmony_ci { 27662306a36Sopenharmony_ci .name = "wkupaon", .base = 0x4ae07800, 27762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon 27862306a36Sopenharmony_ci }, 27962306a36Sopenharmony_ci { 28062306a36Sopenharmony_ci .name = "emu", .base = 0x4ae07a00, 28162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto 28262306a36Sopenharmony_ci }, 28362306a36Sopenharmony_ci { 28462306a36Sopenharmony_ci .name = "device", .base = 0x4ae07c00, 28562306a36Sopenharmony_ci .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, 28662306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci { }, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct omap_prm_data dra7_prm_data[] = { 29262306a36Sopenharmony_ci { 29362306a36Sopenharmony_ci .name = "mpu", .base = 0x4ae06300, 29462306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci { 29762306a36Sopenharmony_ci .name = "dsp1", .base = 0x4ae06400, 29862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 29962306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, 30062306a36Sopenharmony_ci }, 30162306a36Sopenharmony_ci { 30262306a36Sopenharmony_ci .name = "ipu", .base = 0x4ae06500, 30362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 30462306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, 30562306a36Sopenharmony_ci .clkdm_name = "ipu1" 30662306a36Sopenharmony_ci }, 30762306a36Sopenharmony_ci { 30862306a36Sopenharmony_ci .name = "coreaon", .base = 0x4ae06628, 30962306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 31062306a36Sopenharmony_ci }, 31162306a36Sopenharmony_ci { 31262306a36Sopenharmony_ci .name = "core", .base = 0x4ae06700, 31362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 31462306a36Sopenharmony_ci .rstctrl = 0x210, .rstst = 0x214, .rstmap = rst_map_012, 31562306a36Sopenharmony_ci .clkdm_name = "ipu2" 31662306a36Sopenharmony_ci }, 31762306a36Sopenharmony_ci { 31862306a36Sopenharmony_ci .name = "iva", .base = 0x4ae06f00, 31962306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 32062306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, 32162306a36Sopenharmony_ci }, 32262306a36Sopenharmony_ci { 32362306a36Sopenharmony_ci .name = "cam", .base = 0x4ae07000, 32462306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci { 32762306a36Sopenharmony_ci .name = "dss", .base = 0x4ae07100, 32862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 32962306a36Sopenharmony_ci }, 33062306a36Sopenharmony_ci { 33162306a36Sopenharmony_ci .name = "gpu", .base = 0x4ae07200, 33262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 33362306a36Sopenharmony_ci }, 33462306a36Sopenharmony_ci { 33562306a36Sopenharmony_ci .name = "l3init", .base = 0x4ae07300, 33662306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 33762306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, 33862306a36Sopenharmony_ci .clkdm_name = "pcie" 33962306a36Sopenharmony_ci }, 34062306a36Sopenharmony_ci { 34162306a36Sopenharmony_ci .name = "l4per", .base = 0x4ae07400, 34262306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 34362306a36Sopenharmony_ci }, 34462306a36Sopenharmony_ci { 34562306a36Sopenharmony_ci .name = "custefuse", .base = 0x4ae07600, 34662306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 34762306a36Sopenharmony_ci }, 34862306a36Sopenharmony_ci { 34962306a36Sopenharmony_ci .name = "wkupaon", .base = 0x4ae07724, 35062306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 35162306a36Sopenharmony_ci }, 35262306a36Sopenharmony_ci { 35362306a36Sopenharmony_ci .name = "emu", .base = 0x4ae07900, 35462306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 35562306a36Sopenharmony_ci }, 35662306a36Sopenharmony_ci { 35762306a36Sopenharmony_ci .name = "dsp2", .base = 0x4ae07b00, 35862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 35962306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 36062306a36Sopenharmony_ci }, 36162306a36Sopenharmony_ci { 36262306a36Sopenharmony_ci .name = "eve1", .base = 0x4ae07b40, 36362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 36462306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 36562306a36Sopenharmony_ci }, 36662306a36Sopenharmony_ci { 36762306a36Sopenharmony_ci .name = "eve2", .base = 0x4ae07b80, 36862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 36962306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 37062306a36Sopenharmony_ci }, 37162306a36Sopenharmony_ci { 37262306a36Sopenharmony_ci .name = "eve3", .base = 0x4ae07bc0, 37362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 37462306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 37562306a36Sopenharmony_ci }, 37662306a36Sopenharmony_ci { 37762306a36Sopenharmony_ci .name = "eve4", .base = 0x4ae07c00, 37862306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 37962306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 38062306a36Sopenharmony_ci }, 38162306a36Sopenharmony_ci { 38262306a36Sopenharmony_ci .name = "rtc", .base = 0x4ae07c60, 38362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 38462306a36Sopenharmony_ci }, 38562306a36Sopenharmony_ci { 38662306a36Sopenharmony_ci .name = "vpe", .base = 0x4ae07c80, 38762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 38862306a36Sopenharmony_ci }, 38962306a36Sopenharmony_ci { }, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic const struct omap_rst_map am3_per_rst_map[] = { 39362306a36Sopenharmony_ci { .rst = 1 }, 39462306a36Sopenharmony_ci { .rst = -1 }, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic const struct omap_rst_map am3_wkup_rst_map[] = { 39862306a36Sopenharmony_ci { .rst = 3, .st = 5 }, 39962306a36Sopenharmony_ci { .rst = -1 }, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct omap_prm_data am3_prm_data[] = { 40362306a36Sopenharmony_ci { 40462306a36Sopenharmony_ci .name = "per", .base = 0x44e00c00, 40562306a36Sopenharmony_ci .pwrstctrl = 0xc, .pwrstst = 0x8, .dmap = &omap_prm_noinact, 40662306a36Sopenharmony_ci .rstctrl = 0x0, .rstmap = am3_per_rst_map, 40762306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" 40862306a36Sopenharmony_ci }, 40962306a36Sopenharmony_ci { 41062306a36Sopenharmony_ci .name = "wkup", .base = 0x44e00d00, 41162306a36Sopenharmony_ci .pwrstctrl = 0x4, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 41262306a36Sopenharmony_ci .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, 41362306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM 41462306a36Sopenharmony_ci }, 41562306a36Sopenharmony_ci { 41662306a36Sopenharmony_ci .name = "mpu", .base = 0x44e00e00, 41762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 41862306a36Sopenharmony_ci }, 41962306a36Sopenharmony_ci { 42062306a36Sopenharmony_ci .name = "device", .base = 0x44e00f00, 42162306a36Sopenharmony_ci .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, 42262306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM 42362306a36Sopenharmony_ci }, 42462306a36Sopenharmony_ci { 42562306a36Sopenharmony_ci .name = "rtc", .base = 0x44e01000, 42662306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 42762306a36Sopenharmony_ci }, 42862306a36Sopenharmony_ci { 42962306a36Sopenharmony_ci .name = "gfx", .base = 0x44e01100, 43062306a36Sopenharmony_ci .pwrstctrl = 0, .pwrstst = 0x10, .dmap = &omap_prm_noinact, 43162306a36Sopenharmony_ci .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", 43262306a36Sopenharmony_ci }, 43362306a36Sopenharmony_ci { 43462306a36Sopenharmony_ci .name = "cefuse", .base = 0x44e01200, 43562306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 43662306a36Sopenharmony_ci }, 43762306a36Sopenharmony_ci { }, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic const struct omap_rst_map am4_per_rst_map[] = { 44162306a36Sopenharmony_ci { .rst = 1, .st = 0 }, 44262306a36Sopenharmony_ci { .rst = -1 }, 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct omap_rst_map am4_device_rst_map[] = { 44662306a36Sopenharmony_ci { .rst = 0, .st = 1 }, 44762306a36Sopenharmony_ci { .rst = 1, .st = 0 }, 44862306a36Sopenharmony_ci { .rst = -1 }, 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic const struct omap_prm_data am4_prm_data[] = { 45262306a36Sopenharmony_ci { 45362306a36Sopenharmony_ci .name = "mpu", .base = 0x44df0300, 45462306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 45562306a36Sopenharmony_ci }, 45662306a36Sopenharmony_ci { 45762306a36Sopenharmony_ci .name = "gfx", .base = 0x44df0400, 45862306a36Sopenharmony_ci .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 45962306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", 46062306a36Sopenharmony_ci }, 46162306a36Sopenharmony_ci { 46262306a36Sopenharmony_ci .name = "rtc", .base = 0x44df0500, 46362306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 46462306a36Sopenharmony_ci }, 46562306a36Sopenharmony_ci { 46662306a36Sopenharmony_ci .name = "tamper", .base = 0x44df0600, 46762306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 46862306a36Sopenharmony_ci }, 46962306a36Sopenharmony_ci { 47062306a36Sopenharmony_ci .name = "cefuse", .base = 0x44df0700, 47162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, 47262306a36Sopenharmony_ci }, 47362306a36Sopenharmony_ci { 47462306a36Sopenharmony_ci .name = "per", .base = 0x44df0800, 47562306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, 47662306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, 47762306a36Sopenharmony_ci .clkdm_name = "pruss_ocp" 47862306a36Sopenharmony_ci }, 47962306a36Sopenharmony_ci { 48062306a36Sopenharmony_ci .name = "wkup", .base = 0x44df2000, 48162306a36Sopenharmony_ci .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, 48262306a36Sopenharmony_ci .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, 48362306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_NO_CLKDM 48462306a36Sopenharmony_ci }, 48562306a36Sopenharmony_ci { 48662306a36Sopenharmony_ci .name = "device", .base = 0x44df4000, 48762306a36Sopenharmony_ci .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, 48862306a36Sopenharmony_ci .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM 48962306a36Sopenharmony_ci }, 49062306a36Sopenharmony_ci { }, 49162306a36Sopenharmony_ci}; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic const struct of_device_id omap_prm_id_table[] = { 49462306a36Sopenharmony_ci { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, 49562306a36Sopenharmony_ci { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data }, 49662306a36Sopenharmony_ci { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, 49762306a36Sopenharmony_ci { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, 49862306a36Sopenharmony_ci { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, 49962306a36Sopenharmony_ci { }, 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci#ifdef DEBUG 50362306a36Sopenharmony_cistatic void omap_prm_domain_show_state(struct omap_prm_domain *prmd, 50462306a36Sopenharmony_ci const char *desc) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci dev_dbg(prmd->dev, "%s %s: %08x/%08x\n", 50762306a36Sopenharmony_ci prmd->pd.name, desc, 50862306a36Sopenharmony_ci readl_relaxed(prmd->prm->base + prmd->pwrstctrl), 50962306a36Sopenharmony_ci readl_relaxed(prmd->prm->base + prmd->pwrstst)); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci#else 51262306a36Sopenharmony_cistatic inline void omap_prm_domain_show_state(struct omap_prm_domain *prmd, 51362306a36Sopenharmony_ci const char *desc) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci#endif 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int omap_prm_domain_power_on(struct generic_pm_domain *domain) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct omap_prm_domain *prmd; 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci u32 v, mode; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci prmd = genpd_to_prm_domain(domain); 52562306a36Sopenharmony_ci if (!prmd->cap) 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci omap_prm_domain_show_state(prmd, "on: previous state"); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (prmd->pwrstctrl_saved) 53162306a36Sopenharmony_ci v = prmd->pwrstctrl_saved; 53262306a36Sopenharmony_ci else 53362306a36Sopenharmony_ci v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (prmd->prm->data->flags & OMAP_PRM_RET_WHEN_IDLE) 53662306a36Sopenharmony_ci mode = OMAP_PRMD_RETENTION; 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci mode = OMAP_PRMD_ON_ACTIVE; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci writel_relaxed((v & ~PRM_POWERSTATE_MASK) | mode, 54162306a36Sopenharmony_ci prmd->prm->base + prmd->pwrstctrl); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* wait for the transition bit to get cleared */ 54462306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, 54562306a36Sopenharmony_ci v, !(v & PRM_ST_INTRANSITION), 1, 54662306a36Sopenharmony_ci PRM_STATE_MAX_WAIT); 54762306a36Sopenharmony_ci if (ret) 54862306a36Sopenharmony_ci dev_err(prmd->dev, "%s: %s timed out\n", 54962306a36Sopenharmony_ci prmd->pd.name, __func__); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci omap_prm_domain_show_state(prmd, "on: new state"); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* No need to check for holes in the mask for the lowest mode */ 55762306a36Sopenharmony_cistatic int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci return __ffs(prmd->cap->usable_modes); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int omap_prm_domain_power_off(struct generic_pm_domain *domain) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct omap_prm_domain *prmd; 56562306a36Sopenharmony_ci int ret; 56662306a36Sopenharmony_ci u32 v; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci prmd = genpd_to_prm_domain(domain); 56962306a36Sopenharmony_ci if (!prmd->cap) 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci omap_prm_domain_show_state(prmd, "off: previous state"); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); 57562306a36Sopenharmony_ci prmd->pwrstctrl_saved = v; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci v &= ~PRM_POWERSTATE_MASK; 57862306a36Sopenharmony_ci v |= omap_prm_domain_find_lowest(prmd); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (prmd->cap->statechange) 58162306a36Sopenharmony_ci v |= PRM_LOWPOWERSTATECHANGE; 58262306a36Sopenharmony_ci if (prmd->cap->logicretstate) 58362306a36Sopenharmony_ci v &= ~PRM_LOGICRETSTATE; 58462306a36Sopenharmony_ci else 58562306a36Sopenharmony_ci v |= PRM_LOGICRETSTATE; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci writel_relaxed(v, prmd->prm->base + prmd->pwrstctrl); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* wait for the transition bit to get cleared */ 59062306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, 59162306a36Sopenharmony_ci v, !(v & PRM_ST_INTRANSITION), 1, 59262306a36Sopenharmony_ci PRM_STATE_MAX_WAIT); 59362306a36Sopenharmony_ci if (ret) 59462306a36Sopenharmony_ci dev_warn(prmd->dev, "%s: %s timed out\n", 59562306a36Sopenharmony_ci __func__, prmd->pd.name); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci omap_prm_domain_show_state(prmd, "off: new state"); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* 60362306a36Sopenharmony_ci * Note that ti-sysc already manages the module clocks separately so 60462306a36Sopenharmony_ci * no need to manage those. Interconnect instances need clocks managed 60562306a36Sopenharmony_ci * for simple-pm-bus. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_cistatic int omap_prm_domain_attach_clock(struct device *dev, 60862306a36Sopenharmony_ci struct omap_prm_domain *prmd) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct device_node *np = dev->of_node; 61162306a36Sopenharmony_ci int error; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!of_device_is_compatible(np, "simple-pm-bus")) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!of_property_read_bool(np, "clocks")) 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci error = pm_clk_create(dev); 62062306a36Sopenharmony_ci if (error) 62162306a36Sopenharmony_ci return error; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci error = of_pm_clk_add_clks(dev); 62462306a36Sopenharmony_ci if (error < 0) { 62562306a36Sopenharmony_ci pm_clk_destroy(dev); 62662306a36Sopenharmony_ci return error; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci prmd->uses_pm_clk = 1; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int omap_prm_domain_attach_dev(struct generic_pm_domain *domain, 63562306a36Sopenharmony_ci struct device *dev) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct generic_pm_domain_data *genpd_data; 63862306a36Sopenharmony_ci struct of_phandle_args pd_args; 63962306a36Sopenharmony_ci struct omap_prm_domain *prmd; 64062306a36Sopenharmony_ci struct device_node *np; 64162306a36Sopenharmony_ci int ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci prmd = genpd_to_prm_domain(domain); 64462306a36Sopenharmony_ci np = dev->of_node; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ret = of_parse_phandle_with_args(np, "power-domains", 64762306a36Sopenharmony_ci "#power-domain-cells", 0, &pd_args); 64862306a36Sopenharmony_ci if (ret < 0) 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (pd_args.args_count != 0) 65262306a36Sopenharmony_ci dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n", 65362306a36Sopenharmony_ci prmd->pd.name, pd_args.args_count); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci genpd_data = dev_gpd_data(dev); 65662306a36Sopenharmony_ci genpd_data->data = NULL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci ret = omap_prm_domain_attach_clock(dev, prmd); 65962306a36Sopenharmony_ci if (ret) 66062306a36Sopenharmony_ci return ret; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void omap_prm_domain_detach_dev(struct generic_pm_domain *domain, 66662306a36Sopenharmony_ci struct device *dev) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct generic_pm_domain_data *genpd_data; 66962306a36Sopenharmony_ci struct omap_prm_domain *prmd; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci prmd = genpd_to_prm_domain(domain); 67262306a36Sopenharmony_ci if (prmd->uses_pm_clk) 67362306a36Sopenharmony_ci pm_clk_destroy(dev); 67462306a36Sopenharmony_ci genpd_data = dev_gpd_data(dev); 67562306a36Sopenharmony_ci genpd_data->data = NULL; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int omap_prm_domain_init(struct device *dev, struct omap_prm *prm) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct omap_prm_domain *prmd; 68162306a36Sopenharmony_ci struct device_node *np = dev->of_node; 68262306a36Sopenharmony_ci const struct omap_prm_data *data; 68362306a36Sopenharmony_ci const char *name; 68462306a36Sopenharmony_ci int error; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (!of_property_present(dev->of_node, "#power-domain-cells")) 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci of_node_put(dev->of_node); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL); 69262306a36Sopenharmony_ci if (!prmd) 69362306a36Sopenharmony_ci return -ENOMEM; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci data = prm->data; 69662306a36Sopenharmony_ci name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s", 69762306a36Sopenharmony_ci data->name); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci prmd->dev = dev; 70062306a36Sopenharmony_ci prmd->prm = prm; 70162306a36Sopenharmony_ci prmd->cap = prmd->prm->data->dmap; 70262306a36Sopenharmony_ci prmd->pwrstctrl = prmd->prm->data->pwrstctrl; 70362306a36Sopenharmony_ci prmd->pwrstst = prmd->prm->data->pwrstst; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci prmd->pd.name = name; 70662306a36Sopenharmony_ci prmd->pd.power_on = omap_prm_domain_power_on; 70762306a36Sopenharmony_ci prmd->pd.power_off = omap_prm_domain_power_off; 70862306a36Sopenharmony_ci prmd->pd.attach_dev = omap_prm_domain_attach_dev; 70962306a36Sopenharmony_ci prmd->pd.detach_dev = omap_prm_domain_detach_dev; 71062306a36Sopenharmony_ci prmd->pd.flags = GENPD_FLAG_PM_CLK; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci pm_genpd_init(&prmd->pd, NULL, true); 71362306a36Sopenharmony_ci error = of_genpd_add_provider_simple(np, &prmd->pd); 71462306a36Sopenharmony_ci if (error) 71562306a36Sopenharmony_ci pm_genpd_remove(&prmd->pd); 71662306a36Sopenharmony_ci else 71762306a36Sopenharmony_ci prm->prmd = prmd; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return error; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci if (reset->mask & BIT(id)) 72562306a36Sopenharmony_ci return true; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return false; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int omap_reset_get_st_bit(struct omap_reset_data *reset, 73162306a36Sopenharmony_ci unsigned long id) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci const struct omap_rst_map *map = reset->prm->data->rstmap; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci while (map->rst >= 0) { 73662306a36Sopenharmony_ci if (map->rst == id) 73762306a36Sopenharmony_ci return map->st; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci map++; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return id; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int omap_reset_status(struct reset_controller_dev *rcdev, 74662306a36Sopenharmony_ci unsigned long id) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct omap_reset_data *reset = to_omap_reset_data(rcdev); 74962306a36Sopenharmony_ci u32 v; 75062306a36Sopenharmony_ci int st_bit = omap_reset_get_st_bit(reset, id); 75162306a36Sopenharmony_ci bool has_rstst = reset->prm->data->rstst || 75262306a36Sopenharmony_ci (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Check if we have rstst */ 75562306a36Sopenharmony_ci if (!has_rstst) 75662306a36Sopenharmony_ci return -ENOTSUPP; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Check if hw reset line is asserted */ 75962306a36Sopenharmony_ci v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); 76062306a36Sopenharmony_ci if (v & BIT(id)) 76162306a36Sopenharmony_ci return 1; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* 76462306a36Sopenharmony_ci * Check reset status, high value means reset sequence has been 76562306a36Sopenharmony_ci * completed successfully so we can return 0 here (reset deasserted) 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_ci v = readl_relaxed(reset->prm->base + reset->prm->data->rstst); 76862306a36Sopenharmony_ci v >>= st_bit; 76962306a36Sopenharmony_ci v &= 1; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return !v; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int omap_reset_assert(struct reset_controller_dev *rcdev, 77562306a36Sopenharmony_ci unsigned long id) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci struct omap_reset_data *reset = to_omap_reset_data(rcdev); 77862306a36Sopenharmony_ci u32 v; 77962306a36Sopenharmony_ci unsigned long flags; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* assert the reset control line */ 78262306a36Sopenharmony_ci spin_lock_irqsave(&reset->lock, flags); 78362306a36Sopenharmony_ci v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); 78462306a36Sopenharmony_ci v |= 1 << id; 78562306a36Sopenharmony_ci writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); 78662306a36Sopenharmony_ci spin_unlock_irqrestore(&reset->lock, flags); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int omap_reset_deassert(struct reset_controller_dev *rcdev, 79262306a36Sopenharmony_ci unsigned long id) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct omap_reset_data *reset = to_omap_reset_data(rcdev); 79562306a36Sopenharmony_ci u32 v; 79662306a36Sopenharmony_ci int st_bit; 79762306a36Sopenharmony_ci bool has_rstst; 79862306a36Sopenharmony_ci unsigned long flags; 79962306a36Sopenharmony_ci struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev); 80062306a36Sopenharmony_ci int ret = 0; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Nothing to do if the reset is already deasserted */ 80362306a36Sopenharmony_ci if (!omap_reset_status(rcdev, id)) 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci has_rstst = reset->prm->data->rstst || 80762306a36Sopenharmony_ci (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (has_rstst) { 81062306a36Sopenharmony_ci st_bit = omap_reset_get_st_bit(reset, id); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* Clear the reset status by writing 1 to the status bit */ 81362306a36Sopenharmony_ci v = 1 << st_bit; 81462306a36Sopenharmony_ci writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (reset->clkdm) 81862306a36Sopenharmony_ci pdata->clkdm_deny_idle(reset->clkdm); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* de-assert the reset control line */ 82162306a36Sopenharmony_ci spin_lock_irqsave(&reset->lock, flags); 82262306a36Sopenharmony_ci v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); 82362306a36Sopenharmony_ci v &= ~(1 << id); 82462306a36Sopenharmony_ci writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); 82562306a36Sopenharmony_ci spin_unlock_irqrestore(&reset->lock, flags); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* wait for the reset bit to clear */ 82862306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + 82962306a36Sopenharmony_ci reset->prm->data->rstctrl, 83062306a36Sopenharmony_ci v, !(v & BIT(id)), 1, 83162306a36Sopenharmony_ci OMAP_RESET_MAX_WAIT); 83262306a36Sopenharmony_ci if (ret) 83362306a36Sopenharmony_ci pr_err("%s: timedout waiting for %s:%lu\n", __func__, 83462306a36Sopenharmony_ci reset->prm->data->name, id); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* wait for the status to be set */ 83762306a36Sopenharmony_ci if (has_rstst) { 83862306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + 83962306a36Sopenharmony_ci reset->prm->data->rstst, 84062306a36Sopenharmony_ci v, v & BIT(st_bit), 1, 84162306a36Sopenharmony_ci OMAP_RESET_MAX_WAIT); 84262306a36Sopenharmony_ci if (ret) 84362306a36Sopenharmony_ci pr_err("%s: timedout waiting for %s:%lu\n", __func__, 84462306a36Sopenharmony_ci reset->prm->data->name, id); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (reset->clkdm) 84862306a36Sopenharmony_ci pdata->clkdm_allow_idle(reset->clkdm); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic const struct reset_control_ops omap_reset_ops = { 85462306a36Sopenharmony_ci .assert = omap_reset_assert, 85562306a36Sopenharmony_ci .deassert = omap_reset_deassert, 85662306a36Sopenharmony_ci .status = omap_reset_status, 85762306a36Sopenharmony_ci}; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int omap_prm_reset_xlate(struct reset_controller_dev *rcdev, 86062306a36Sopenharmony_ci const struct of_phandle_args *reset_spec) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct omap_reset_data *reset = to_omap_reset_data(rcdev); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!_is_valid_reset(reset, reset_spec->args[0])) 86562306a36Sopenharmony_ci return -EINVAL; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return reset_spec->args[0]; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int omap_prm_reset_init(struct platform_device *pdev, 87162306a36Sopenharmony_ci struct omap_prm *prm) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct omap_reset_data *reset; 87462306a36Sopenharmony_ci const struct omap_rst_map *map; 87562306a36Sopenharmony_ci struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev); 87662306a36Sopenharmony_ci char buf[32]; 87762306a36Sopenharmony_ci u32 v; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * Check if we have controllable resets. If either rstctrl is non-zero 88162306a36Sopenharmony_ci * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register 88262306a36Sopenharmony_ci * for the domain. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Check if we have the pdata callbacks in place */ 88862306a36Sopenharmony_ci if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle || 88962306a36Sopenharmony_ci !pdata->clkdm_allow_idle) 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci map = prm->data->rstmap; 89362306a36Sopenharmony_ci if (!map) 89462306a36Sopenharmony_ci return -EINVAL; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); 89762306a36Sopenharmony_ci if (!reset) 89862306a36Sopenharmony_ci return -ENOMEM; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci reset->rcdev.owner = THIS_MODULE; 90162306a36Sopenharmony_ci reset->rcdev.ops = &omap_reset_ops; 90262306a36Sopenharmony_ci reset->rcdev.of_node = pdev->dev.of_node; 90362306a36Sopenharmony_ci reset->rcdev.nr_resets = OMAP_MAX_RESETS; 90462306a36Sopenharmony_ci reset->rcdev.of_xlate = omap_prm_reset_xlate; 90562306a36Sopenharmony_ci reset->rcdev.of_reset_n_cells = 1; 90662306a36Sopenharmony_ci reset->dev = &pdev->dev; 90762306a36Sopenharmony_ci spin_lock_init(&reset->lock); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci reset->prm = prm; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name : 91262306a36Sopenharmony_ci prm->data->name); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) { 91562306a36Sopenharmony_ci reset->clkdm = pdata->clkdm_lookup(buf); 91662306a36Sopenharmony_ci if (!reset->clkdm) 91762306a36Sopenharmony_ci return -EINVAL; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci while (map->rst >= 0) { 92162306a36Sopenharmony_ci reset->mask |= BIT(map->rst); 92262306a36Sopenharmony_ci map++; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Quirk handling to assert rst_map_012 bits on reset and avoid errors */ 92662306a36Sopenharmony_ci if (prm->data->rstmap == rst_map_012) { 92762306a36Sopenharmony_ci v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); 92862306a36Sopenharmony_ci if ((v & reset->mask) != reset->mask) { 92962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Asserting all resets: %08x\n", v); 93062306a36Sopenharmony_ci writel_relaxed(reset->mask, reset->prm->base + 93162306a36Sopenharmony_ci reset->prm->data->rstctrl); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return devm_reset_controller_register(&pdev->dev, &reset->rcdev); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int omap_prm_probe(struct platform_device *pdev) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct resource *res; 94162306a36Sopenharmony_ci const struct omap_prm_data *data; 94262306a36Sopenharmony_ci struct omap_prm *prm; 94362306a36Sopenharmony_ci int ret; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 94662306a36Sopenharmony_ci if (!data) 94762306a36Sopenharmony_ci return -ENOTSUPP; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); 95062306a36Sopenharmony_ci if (!prm) 95162306a36Sopenharmony_ci return -ENOMEM; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci prm->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 95462306a36Sopenharmony_ci if (IS_ERR(prm->base)) 95562306a36Sopenharmony_ci return PTR_ERR(prm->base); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci while (data->base != res->start) { 95862306a36Sopenharmony_ci if (!data->base) 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci data++; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci prm->data = data; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ret = omap_prm_domain_init(&pdev->dev, prm); 96662306a36Sopenharmony_ci if (ret) 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci ret = omap_prm_reset_init(pdev, prm); 97062306a36Sopenharmony_ci if (ret) 97162306a36Sopenharmony_ci goto err_domain; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cierr_domain: 97662306a36Sopenharmony_ci of_genpd_del_provider(pdev->dev.of_node); 97762306a36Sopenharmony_ci pm_genpd_remove(&prm->prmd->pd); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic struct platform_driver omap_prm_driver = { 98362306a36Sopenharmony_ci .probe = omap_prm_probe, 98462306a36Sopenharmony_ci .driver = { 98562306a36Sopenharmony_ci .name = KBUILD_MODNAME, 98662306a36Sopenharmony_ci .of_match_table = omap_prm_id_table, 98762306a36Sopenharmony_ci }, 98862306a36Sopenharmony_ci}; 98962306a36Sopenharmony_cibuiltin_platform_driver(omap_prm_driver); 990