162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ti-sysc.c - Texas Instruments sysc interconnect target driver 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/clkdev.h> 962306a36Sopenharmony_ci#include <linux/cpu_pm.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/pm_domain.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/reset.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/of_platform.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/sys_soc.h> 2162306a36Sopenharmony_ci#include <linux/timekeeping.h> 2262306a36Sopenharmony_ci#include <linux/iopoll.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/platform_data/ti-sysc.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <dt-bindings/bus/ti-sysc.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DIS_ISP BIT(2) 2962306a36Sopenharmony_ci#define DIS_IVA BIT(1) 3062306a36Sopenharmony_ci#define DIS_SGX BIT(0) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MAX_MODULE_SOFTRESET_WAIT 10000 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cienum sysc_soc { 3762306a36Sopenharmony_ci SOC_UNKNOWN, 3862306a36Sopenharmony_ci SOC_2420, 3962306a36Sopenharmony_ci SOC_2430, 4062306a36Sopenharmony_ci SOC_3430, 4162306a36Sopenharmony_ci SOC_AM35, 4262306a36Sopenharmony_ci SOC_3630, 4362306a36Sopenharmony_ci SOC_4430, 4462306a36Sopenharmony_ci SOC_4460, 4562306a36Sopenharmony_ci SOC_4470, 4662306a36Sopenharmony_ci SOC_5430, 4762306a36Sopenharmony_ci SOC_AM3, 4862306a36Sopenharmony_ci SOC_AM4, 4962306a36Sopenharmony_ci SOC_DRA7, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct sysc_address { 5362306a36Sopenharmony_ci unsigned long base; 5462306a36Sopenharmony_ci struct list_head node; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct sysc_module { 5862306a36Sopenharmony_ci struct sysc *ddata; 5962306a36Sopenharmony_ci struct list_head node; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct sysc_soc_info { 6362306a36Sopenharmony_ci unsigned long general_purpose:1; 6462306a36Sopenharmony_ci enum sysc_soc soc; 6562306a36Sopenharmony_ci struct mutex list_lock; /* disabled and restored modules list lock */ 6662306a36Sopenharmony_ci struct list_head disabled_modules; 6762306a36Sopenharmony_ci struct list_head restored_modules; 6862306a36Sopenharmony_ci struct notifier_block nb; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cienum sysc_clocks { 7262306a36Sopenharmony_ci SYSC_FCK, 7362306a36Sopenharmony_ci SYSC_ICK, 7462306a36Sopenharmony_ci SYSC_OPTFCK0, 7562306a36Sopenharmony_ci SYSC_OPTFCK1, 7662306a36Sopenharmony_ci SYSC_OPTFCK2, 7762306a36Sopenharmony_ci SYSC_OPTFCK3, 7862306a36Sopenharmony_ci SYSC_OPTFCK4, 7962306a36Sopenharmony_ci SYSC_OPTFCK5, 8062306a36Sopenharmony_ci SYSC_OPTFCK6, 8162306a36Sopenharmony_ci SYSC_OPTFCK7, 8262306a36Sopenharmony_ci SYSC_MAX_CLOCKS, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic struct sysc_soc_info *sysc_soc; 8662306a36Sopenharmony_cistatic const char * const reg_names[] = { "rev", "sysc", "syss", }; 8762306a36Sopenharmony_cistatic const char * const clock_names[SYSC_MAX_CLOCKS] = { 8862306a36Sopenharmony_ci "fck", "ick", "opt0", "opt1", "opt2", "opt3", "opt4", 8962306a36Sopenharmony_ci "opt5", "opt6", "opt7", 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define SYSC_IDLEMODE_MASK 3 9362306a36Sopenharmony_ci#define SYSC_CLOCKACTIVITY_MASK 3 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * struct sysc - TI sysc interconnect target module registers and capabilities 9762306a36Sopenharmony_ci * @dev: struct device pointer 9862306a36Sopenharmony_ci * @module_pa: physical address of the interconnect target module 9962306a36Sopenharmony_ci * @module_size: size of the interconnect target module 10062306a36Sopenharmony_ci * @module_va: virtual address of the interconnect target module 10162306a36Sopenharmony_ci * @offsets: register offsets from module base 10262306a36Sopenharmony_ci * @mdata: ti-sysc to hwmod translation data for a module 10362306a36Sopenharmony_ci * @clocks: clocks used by the interconnect target module 10462306a36Sopenharmony_ci * @clock_roles: clock role names for the found clocks 10562306a36Sopenharmony_ci * @nr_clocks: number of clocks used by the interconnect target module 10662306a36Sopenharmony_ci * @rsts: resets used by the interconnect target module 10762306a36Sopenharmony_ci * @legacy_mode: configured for legacy mode if set 10862306a36Sopenharmony_ci * @cap: interconnect target module capabilities 10962306a36Sopenharmony_ci * @cfg: interconnect target module configuration 11062306a36Sopenharmony_ci * @cookie: data used by legacy platform callbacks 11162306a36Sopenharmony_ci * @name: name if available 11262306a36Sopenharmony_ci * @revision: interconnect target module revision 11362306a36Sopenharmony_ci * @sysconfig: saved sysconfig register value 11462306a36Sopenharmony_ci * @reserved: target module is reserved and already in use 11562306a36Sopenharmony_ci * @enabled: sysc runtime enabled status 11662306a36Sopenharmony_ci * @needs_resume: runtime resume needed on resume from suspend 11762306a36Sopenharmony_ci * @child_needs_resume: runtime resume needed for child on resume from suspend 11862306a36Sopenharmony_ci * @disable_on_idle: status flag used for disabling modules with resets 11962306a36Sopenharmony_ci * @idle_work: work structure used to perform delayed idle on a module 12062306a36Sopenharmony_ci * @pre_reset_quirk: module specific pre-reset quirk 12162306a36Sopenharmony_ci * @post_reset_quirk: module specific post-reset quirk 12262306a36Sopenharmony_ci * @reset_done_quirk: module specific reset done quirk 12362306a36Sopenharmony_ci * @module_enable_quirk: module specific enable quirk 12462306a36Sopenharmony_ci * @module_disable_quirk: module specific disable quirk 12562306a36Sopenharmony_ci * @module_unlock_quirk: module specific sysconfig unlock quirk 12662306a36Sopenharmony_ci * @module_lock_quirk: module specific sysconfig lock quirk 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistruct sysc { 12962306a36Sopenharmony_ci struct device *dev; 13062306a36Sopenharmony_ci u64 module_pa; 13162306a36Sopenharmony_ci u32 module_size; 13262306a36Sopenharmony_ci void __iomem *module_va; 13362306a36Sopenharmony_ci int offsets[SYSC_MAX_REGS]; 13462306a36Sopenharmony_ci struct ti_sysc_module_data *mdata; 13562306a36Sopenharmony_ci struct clk **clocks; 13662306a36Sopenharmony_ci const char **clock_roles; 13762306a36Sopenharmony_ci int nr_clocks; 13862306a36Sopenharmony_ci struct reset_control *rsts; 13962306a36Sopenharmony_ci const char *legacy_mode; 14062306a36Sopenharmony_ci const struct sysc_capabilities *cap; 14162306a36Sopenharmony_ci struct sysc_config cfg; 14262306a36Sopenharmony_ci struct ti_sysc_cookie cookie; 14362306a36Sopenharmony_ci const char *name; 14462306a36Sopenharmony_ci u32 revision; 14562306a36Sopenharmony_ci u32 sysconfig; 14662306a36Sopenharmony_ci unsigned int reserved:1; 14762306a36Sopenharmony_ci unsigned int enabled:1; 14862306a36Sopenharmony_ci unsigned int needs_resume:1; 14962306a36Sopenharmony_ci unsigned int child_needs_resume:1; 15062306a36Sopenharmony_ci struct delayed_work idle_work; 15162306a36Sopenharmony_ci void (*pre_reset_quirk)(struct sysc *sysc); 15262306a36Sopenharmony_ci void (*post_reset_quirk)(struct sysc *sysc); 15362306a36Sopenharmony_ci void (*reset_done_quirk)(struct sysc *sysc); 15462306a36Sopenharmony_ci void (*module_enable_quirk)(struct sysc *sysc); 15562306a36Sopenharmony_ci void (*module_disable_quirk)(struct sysc *sysc); 15662306a36Sopenharmony_ci void (*module_unlock_quirk)(struct sysc *sysc); 15762306a36Sopenharmony_ci void (*module_lock_quirk)(struct sysc *sysc); 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, 16162306a36Sopenharmony_ci bool is_child); 16262306a36Sopenharmony_cistatic int sysc_reset(struct sysc *ddata); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void sysc_write(struct sysc *ddata, int offset, u32 value) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { 16762306a36Sopenharmony_ci writew_relaxed(value & 0xffff, ddata->module_va + offset); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Only i2c revision has LO and HI register with stride of 4 */ 17062306a36Sopenharmony_ci if (ddata->offsets[SYSC_REVISION] >= 0 && 17162306a36Sopenharmony_ci offset == ddata->offsets[SYSC_REVISION]) { 17262306a36Sopenharmony_ci u16 hi = value >> 16; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci writew_relaxed(hi, ddata->module_va + offset + 4); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci writel_relaxed(value, ddata->module_va + offset); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic u32 sysc_read(struct sysc *ddata, int offset) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { 18662306a36Sopenharmony_ci u32 val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci val = readw_relaxed(ddata->module_va + offset); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Only i2c revision has LO and HI register with stride of 4 */ 19162306a36Sopenharmony_ci if (ddata->offsets[SYSC_REVISION] >= 0 && 19262306a36Sopenharmony_ci offset == ddata->offsets[SYSC_REVISION]) { 19362306a36Sopenharmony_ci u16 tmp = readw_relaxed(ddata->module_va + offset + 4); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci val |= tmp << 16; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return val; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return readl_relaxed(ddata->module_va + offset); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic bool sysc_opt_clks_needed(struct sysc *ddata) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return !!(ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_NEEDED); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic u32 sysc_read_revision(struct sysc *ddata) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int offset = ddata->offsets[SYSC_REVISION]; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (offset < 0) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return sysc_read(ddata, offset); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic u32 sysc_read_sysconfig(struct sysc *ddata) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int offset = ddata->offsets[SYSC_SYSCONFIG]; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (offset < 0) 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return sysc_read(ddata, offset); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic u32 sysc_read_sysstatus(struct sysc *ddata) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int offset = ddata->offsets[SYSC_SYSSTATUS]; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (offset < 0) 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return sysc_read(ddata, offset); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int sysc_poll_reset_sysstatus(struct sysc *ddata) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci int error, retries; 24262306a36Sopenharmony_ci u32 syss_done, rstval; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED) 24562306a36Sopenharmony_ci syss_done = 0; 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci syss_done = ddata->cfg.syss_mask; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (likely(!timekeeping_suspended)) { 25062306a36Sopenharmony_ci error = readx_poll_timeout_atomic(sysc_read_sysstatus, ddata, 25162306a36Sopenharmony_ci rstval, (rstval & ddata->cfg.syss_mask) == 25262306a36Sopenharmony_ci syss_done, 100, MAX_MODULE_SOFTRESET_WAIT); 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci retries = MAX_MODULE_SOFTRESET_WAIT; 25562306a36Sopenharmony_ci while (retries--) { 25662306a36Sopenharmony_ci rstval = sysc_read_sysstatus(ddata); 25762306a36Sopenharmony_ci if ((rstval & ddata->cfg.syss_mask) == syss_done) 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci udelay(2); /* Account for udelay flakeyness */ 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci error = -ETIMEDOUT; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return error; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int sysc_poll_reset_sysconfig(struct sysc *ddata) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int error, retries; 27062306a36Sopenharmony_ci u32 sysc_mask, rstval; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci sysc_mask = BIT(ddata->cap->regbits->srst_shift); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (likely(!timekeeping_suspended)) { 27562306a36Sopenharmony_ci error = readx_poll_timeout_atomic(sysc_read_sysconfig, ddata, 27662306a36Sopenharmony_ci rstval, !(rstval & sysc_mask), 27762306a36Sopenharmony_ci 100, MAX_MODULE_SOFTRESET_WAIT); 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci retries = MAX_MODULE_SOFTRESET_WAIT; 28062306a36Sopenharmony_ci while (retries--) { 28162306a36Sopenharmony_ci rstval = sysc_read_sysconfig(ddata); 28262306a36Sopenharmony_ci if (!(rstval & sysc_mask)) 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci udelay(2); /* Account for udelay flakeyness */ 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci error = -ETIMEDOUT; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return error; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* Poll on reset status */ 29362306a36Sopenharmony_cistatic int sysc_wait_softreset(struct sysc *ddata) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int syss_offset, error = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (ddata->cap->regbits->srst_shift < 0) 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci syss_offset = ddata->offsets[SYSC_SYSSTATUS]; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (syss_offset >= 0) 30362306a36Sopenharmony_ci error = sysc_poll_reset_sysstatus(ddata); 30462306a36Sopenharmony_ci else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) 30562306a36Sopenharmony_ci error = sysc_poll_reset_sysconfig(ddata); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return error; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int sysc_add_named_clock_from_child(struct sysc *ddata, 31162306a36Sopenharmony_ci const char *name, 31262306a36Sopenharmony_ci const char *optfck_name) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 31562306a36Sopenharmony_ci struct device_node *child; 31662306a36Sopenharmony_ci struct clk_lookup *cl; 31762306a36Sopenharmony_ci struct clk *clock; 31862306a36Sopenharmony_ci const char *n; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (name) 32162306a36Sopenharmony_ci n = name; 32262306a36Sopenharmony_ci else 32362306a36Sopenharmony_ci n = optfck_name; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Does the clock alias already exist? */ 32662306a36Sopenharmony_ci clock = of_clk_get_by_name(np, n); 32762306a36Sopenharmony_ci if (!IS_ERR(clock)) { 32862306a36Sopenharmony_ci clk_put(clock); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci child = of_get_next_available_child(np, NULL); 33462306a36Sopenharmony_ci if (!child) 33562306a36Sopenharmony_ci return -ENODEV; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci clock = devm_get_clk_from_child(ddata->dev, child, name); 33862306a36Sopenharmony_ci if (IS_ERR(clock)) 33962306a36Sopenharmony_ci return PTR_ERR(clock); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * Use clkdev_add() instead of clkdev_alloc() to avoid the MAX_DEV_ID 34362306a36Sopenharmony_ci * limit for clk_get(). If cl ever needs to be freed, it should be done 34462306a36Sopenharmony_ci * with clkdev_drop(). 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci cl = kzalloc(sizeof(*cl), GFP_KERNEL); 34762306a36Sopenharmony_ci if (!cl) 34862306a36Sopenharmony_ci return -ENOMEM; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci cl->con_id = n; 35162306a36Sopenharmony_ci cl->dev_id = dev_name(ddata->dev); 35262306a36Sopenharmony_ci cl->clk = clock; 35362306a36Sopenharmony_ci clkdev_add(cl); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci clk_put(clock); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int sysc_init_ext_opt_clock(struct sysc *ddata, const char *name) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci const char *optfck_name; 36362306a36Sopenharmony_ci int error, index; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (ddata->nr_clocks < SYSC_OPTFCK0) 36662306a36Sopenharmony_ci index = SYSC_OPTFCK0; 36762306a36Sopenharmony_ci else 36862306a36Sopenharmony_ci index = ddata->nr_clocks; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (name) 37162306a36Sopenharmony_ci optfck_name = name; 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci optfck_name = clock_names[index]; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci error = sysc_add_named_clock_from_child(ddata, name, optfck_name); 37662306a36Sopenharmony_ci if (error) 37762306a36Sopenharmony_ci return error; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ddata->clock_roles[index] = optfck_name; 38062306a36Sopenharmony_ci ddata->nr_clocks++; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int sysc_get_one_clock(struct sysc *ddata, const char *name) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int error, i, index = -ENODEV; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!strncmp(clock_names[SYSC_FCK], name, 3)) 39062306a36Sopenharmony_ci index = SYSC_FCK; 39162306a36Sopenharmony_ci else if (!strncmp(clock_names[SYSC_ICK], name, 3)) 39262306a36Sopenharmony_ci index = SYSC_ICK; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (index < 0) { 39562306a36Sopenharmony_ci for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) { 39662306a36Sopenharmony_ci if (!ddata->clocks[i]) { 39762306a36Sopenharmony_ci index = i; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (index < 0) { 40462306a36Sopenharmony_ci dev_err(ddata->dev, "clock %s not added\n", name); 40562306a36Sopenharmony_ci return index; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ddata->clocks[index] = devm_clk_get(ddata->dev, name); 40962306a36Sopenharmony_ci if (IS_ERR(ddata->clocks[index])) { 41062306a36Sopenharmony_ci dev_err(ddata->dev, "clock get error for %s: %li\n", 41162306a36Sopenharmony_ci name, PTR_ERR(ddata->clocks[index])); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return PTR_ERR(ddata->clocks[index]); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci error = clk_prepare(ddata->clocks[index]); 41762306a36Sopenharmony_ci if (error) { 41862306a36Sopenharmony_ci dev_err(ddata->dev, "clock prepare error for %s: %i\n", 41962306a36Sopenharmony_ci name, error); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return error; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int sysc_get_clocks(struct sysc *ddata) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 43062306a36Sopenharmony_ci struct property *prop; 43162306a36Sopenharmony_ci const char *name; 43262306a36Sopenharmony_ci int nr_fck = 0, nr_ick = 0, i, error = 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ddata->clock_roles = devm_kcalloc(ddata->dev, 43562306a36Sopenharmony_ci SYSC_MAX_CLOCKS, 43662306a36Sopenharmony_ci sizeof(*ddata->clock_roles), 43762306a36Sopenharmony_ci GFP_KERNEL); 43862306a36Sopenharmony_ci if (!ddata->clock_roles) 43962306a36Sopenharmony_ci return -ENOMEM; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci of_property_for_each_string(np, "clock-names", prop, name) { 44262306a36Sopenharmony_ci if (!strncmp(clock_names[SYSC_FCK], name, 3)) 44362306a36Sopenharmony_ci nr_fck++; 44462306a36Sopenharmony_ci if (!strncmp(clock_names[SYSC_ICK], name, 3)) 44562306a36Sopenharmony_ci nr_ick++; 44662306a36Sopenharmony_ci ddata->clock_roles[ddata->nr_clocks] = name; 44762306a36Sopenharmony_ci ddata->nr_clocks++; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (ddata->nr_clocks < 1) 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if ((ddata->cfg.quirks & SYSC_QUIRK_EXT_OPT_CLOCK)) { 45462306a36Sopenharmony_ci error = sysc_init_ext_opt_clock(ddata, NULL); 45562306a36Sopenharmony_ci if (error) 45662306a36Sopenharmony_ci return error; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (ddata->nr_clocks > SYSC_MAX_CLOCKS) { 46062306a36Sopenharmony_ci dev_err(ddata->dev, "too many clocks for %pOF\n", np); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (nr_fck > 1 || nr_ick > 1) { 46662306a36Sopenharmony_ci dev_err(ddata->dev, "max one fck and ick for %pOF\n", np); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return -EINVAL; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Always add a slot for main clocks fck and ick even if unused */ 47262306a36Sopenharmony_ci if (!nr_fck) 47362306a36Sopenharmony_ci ddata->nr_clocks++; 47462306a36Sopenharmony_ci if (!nr_ick) 47562306a36Sopenharmony_ci ddata->nr_clocks++; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ddata->clocks = devm_kcalloc(ddata->dev, 47862306a36Sopenharmony_ci ddata->nr_clocks, sizeof(*ddata->clocks), 47962306a36Sopenharmony_ci GFP_KERNEL); 48062306a36Sopenharmony_ci if (!ddata->clocks) 48162306a36Sopenharmony_ci return -ENOMEM; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < SYSC_MAX_CLOCKS; i++) { 48462306a36Sopenharmony_ci const char *name = ddata->clock_roles[i]; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!name) 48762306a36Sopenharmony_ci continue; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci error = sysc_get_one_clock(ddata, name); 49062306a36Sopenharmony_ci if (error) 49162306a36Sopenharmony_ci return error; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int sysc_enable_main_clocks(struct sysc *ddata) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct clk *clock; 50062306a36Sopenharmony_ci int i, error; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!ddata->clocks) 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < SYSC_OPTFCK0; i++) { 50662306a36Sopenharmony_ci clock = ddata->clocks[i]; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Main clocks may not have ick */ 50962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 51062306a36Sopenharmony_ci continue; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci error = clk_enable(clock); 51362306a36Sopenharmony_ci if (error) 51462306a36Sopenharmony_ci goto err_disable; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cierr_disable: 52062306a36Sopenharmony_ci for (i--; i >= 0; i--) { 52162306a36Sopenharmony_ci clock = ddata->clocks[i]; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Main clocks may not have ick */ 52462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 52562306a36Sopenharmony_ci continue; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci clk_disable(clock); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return error; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void sysc_disable_main_clocks(struct sysc *ddata) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct clk *clock; 53662306a36Sopenharmony_ci int i; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (!ddata->clocks) 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci for (i = 0; i < SYSC_OPTFCK0; i++) { 54262306a36Sopenharmony_ci clock = ddata->clocks[i]; 54362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci clk_disable(clock); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int sysc_enable_opt_clocks(struct sysc *ddata) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct clk *clock; 55362306a36Sopenharmony_ci int i, error; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!ddata->clocks || ddata->nr_clocks < SYSC_OPTFCK0 + 1) 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) { 55962306a36Sopenharmony_ci clock = ddata->clocks[i]; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Assume no holes for opt clocks */ 56262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci error = clk_enable(clock); 56662306a36Sopenharmony_ci if (error) 56762306a36Sopenharmony_ci goto err_disable; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cierr_disable: 57362306a36Sopenharmony_ci for (i--; i >= 0; i--) { 57462306a36Sopenharmony_ci clock = ddata->clocks[i]; 57562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 57662306a36Sopenharmony_ci continue; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci clk_disable(clock); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return error; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void sysc_disable_opt_clocks(struct sysc *ddata) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct clk *clock; 58762306a36Sopenharmony_ci int i; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!ddata->clocks || ddata->nr_clocks < SYSC_OPTFCK0 + 1) 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) { 59362306a36Sopenharmony_ci clock = ddata->clocks[i]; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Assume no holes for opt clocks */ 59662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clock)) 59762306a36Sopenharmony_ci return; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci clk_disable(clock); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void sysc_clkdm_deny_idle(struct sysc *ddata) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO)) 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci pdata = dev_get_platdata(ddata->dev); 61162306a36Sopenharmony_ci if (pdata && pdata->clkdm_deny_idle) 61262306a36Sopenharmony_ci pdata->clkdm_deny_idle(ddata->dev, &ddata->cookie); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic void sysc_clkdm_allow_idle(struct sysc *ddata) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO)) 62062306a36Sopenharmony_ci return; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci pdata = dev_get_platdata(ddata->dev); 62362306a36Sopenharmony_ci if (pdata && pdata->clkdm_allow_idle) 62462306a36Sopenharmony_ci pdata->clkdm_allow_idle(ddata->dev, &ddata->cookie); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/** 62862306a36Sopenharmony_ci * sysc_init_resets - init rstctrl reset line if configured 62962306a36Sopenharmony_ci * @ddata: device driver data 63062306a36Sopenharmony_ci * 63162306a36Sopenharmony_ci * See sysc_rstctrl_reset_deassert(). 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_cistatic int sysc_init_resets(struct sysc *ddata) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci ddata->rsts = 63662306a36Sopenharmony_ci devm_reset_control_get_optional_shared(ddata->dev, "rstctrl"); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(ddata->rsts); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/** 64262306a36Sopenharmony_ci * sysc_parse_and_check_child_range - parses module IO region from ranges 64362306a36Sopenharmony_ci * @ddata: device driver data 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * In general we only need rev, syss, and sysc registers and not the whole 64662306a36Sopenharmony_ci * module range. But we do want the offsets for these registers from the 64762306a36Sopenharmony_ci * module base. This allows us to check them against the legacy hwmod 64862306a36Sopenharmony_ci * platform data. Let's also check the ranges are configured properly. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_cistatic int sysc_parse_and_check_child_range(struct sysc *ddata) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 65362306a36Sopenharmony_ci struct of_range_parser parser; 65462306a36Sopenharmony_ci struct of_range range; 65562306a36Sopenharmony_ci int error; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci error = of_range_parser_init(&parser, np); 65862306a36Sopenharmony_ci if (error) 65962306a36Sopenharmony_ci return error; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 66262306a36Sopenharmony_ci ddata->module_pa = range.cpu_addr; 66362306a36Sopenharmony_ci ddata->module_size = range.size; 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* Interconnect instances to probe before l4_per instances */ 67162306a36Sopenharmony_cistatic struct resource early_bus_ranges[] = { 67262306a36Sopenharmony_ci /* am3/4 l4_wkup */ 67362306a36Sopenharmony_ci { .start = 0x44c00000, .end = 0x44c00000 + 0x300000, }, 67462306a36Sopenharmony_ci /* omap4/5 and dra7 l4_cfg */ 67562306a36Sopenharmony_ci { .start = 0x4a000000, .end = 0x4a000000 + 0x300000, }, 67662306a36Sopenharmony_ci /* omap4 l4_wkup */ 67762306a36Sopenharmony_ci { .start = 0x4a300000, .end = 0x4a300000 + 0x30000, }, 67862306a36Sopenharmony_ci /* omap5 and dra7 l4_wkup without dra7 dcan segment */ 67962306a36Sopenharmony_ci { .start = 0x4ae00000, .end = 0x4ae00000 + 0x30000, }, 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic atomic_t sysc_defer = ATOMIC_INIT(10); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/** 68562306a36Sopenharmony_ci * sysc_defer_non_critical - defer non_critical interconnect probing 68662306a36Sopenharmony_ci * @ddata: device driver data 68762306a36Sopenharmony_ci * 68862306a36Sopenharmony_ci * We want to probe l4_cfg and l4_wkup interconnect instances before any 68962306a36Sopenharmony_ci * l4_per instances as l4_per instances depend on resources on l4_cfg and 69062306a36Sopenharmony_ci * l4_wkup interconnects. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_cistatic int sysc_defer_non_critical(struct sysc *ddata) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct resource *res; 69562306a36Sopenharmony_ci int i; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (!atomic_read(&sysc_defer)) 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(early_bus_ranges); i++) { 70162306a36Sopenharmony_ci res = &early_bus_ranges[i]; 70262306a36Sopenharmony_ci if (ddata->module_pa >= res->start && 70362306a36Sopenharmony_ci ddata->module_pa <= res->end) { 70462306a36Sopenharmony_ci atomic_set(&sysc_defer, 0); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci atomic_dec_if_positive(&sysc_defer); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return -EPROBE_DEFER; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic struct device_node *stdout_path; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic void sysc_init_stdout_path(struct sysc *ddata) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct device_node *np = NULL; 72062306a36Sopenharmony_ci const char *uart; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (IS_ERR(stdout_path)) 72362306a36Sopenharmony_ci return; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (stdout_path) 72662306a36Sopenharmony_ci return; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci np = of_find_node_by_path("/chosen"); 72962306a36Sopenharmony_ci if (!np) 73062306a36Sopenharmony_ci goto err; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci uart = of_get_property(np, "stdout-path", NULL); 73362306a36Sopenharmony_ci if (!uart) 73462306a36Sopenharmony_ci goto err; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci np = of_find_node_by_path(uart); 73762306a36Sopenharmony_ci if (!np) 73862306a36Sopenharmony_ci goto err; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci stdout_path = np; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cierr: 74562306a36Sopenharmony_ci stdout_path = ERR_PTR(-ENODEV); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void sysc_check_quirk_stdout(struct sysc *ddata, 74962306a36Sopenharmony_ci struct device_node *np) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci sysc_init_stdout_path(ddata); 75262306a36Sopenharmony_ci if (np != stdout_path) 75362306a36Sopenharmony_ci return; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ddata->cfg.quirks |= SYSC_QUIRK_NO_IDLE_ON_INIT | 75662306a36Sopenharmony_ci SYSC_QUIRK_NO_RESET_ON_INIT; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/** 76062306a36Sopenharmony_ci * sysc_check_one_child - check child configuration 76162306a36Sopenharmony_ci * @ddata: device driver data 76262306a36Sopenharmony_ci * @np: child device node 76362306a36Sopenharmony_ci * 76462306a36Sopenharmony_ci * Let's avoid messy situations where we have new interconnect target 76562306a36Sopenharmony_ci * node but children have "ti,hwmods". These belong to the interconnect 76662306a36Sopenharmony_ci * target node and are managed by this driver. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_cistatic void sysc_check_one_child(struct sysc *ddata, 76962306a36Sopenharmony_ci struct device_node *np) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci const char *name; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci name = of_get_property(np, "ti,hwmods", NULL); 77462306a36Sopenharmony_ci if (name && !of_device_is_compatible(np, "ti,sysc")) 77562306a36Sopenharmony_ci dev_warn(ddata->dev, "really a child ti,hwmods property?"); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci sysc_check_quirk_stdout(ddata, np); 77862306a36Sopenharmony_ci sysc_parse_dts_quirks(ddata, np, true); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic void sysc_check_children(struct sysc *ddata) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct device_node *child; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci for_each_child_of_node(ddata->dev->of_node, child) 78662306a36Sopenharmony_ci sysc_check_one_child(ddata, child); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/* 79062306a36Sopenharmony_ci * So far only I2C uses 16-bit read access with clockactivity with revision 79162306a36Sopenharmony_ci * in two registers with stride of 4. We can detect this based on the rev 79262306a36Sopenharmony_ci * register size to configure things far enough to be able to properly read 79362306a36Sopenharmony_ci * the revision register. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_cistatic void sysc_check_quirk_16bit(struct sysc *ddata, struct resource *res) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci if (resource_size(res) == 8) 79862306a36Sopenharmony_ci ddata->cfg.quirks |= SYSC_QUIRK_16BIT | SYSC_QUIRK_USE_CLOCKACT; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/** 80262306a36Sopenharmony_ci * sysc_parse_one - parses the interconnect target module registers 80362306a36Sopenharmony_ci * @ddata: device driver data 80462306a36Sopenharmony_ci * @reg: register to parse 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_cistatic int sysc_parse_one(struct sysc *ddata, enum sysc_registers reg) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct resource *res; 80962306a36Sopenharmony_ci const char *name; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci switch (reg) { 81262306a36Sopenharmony_ci case SYSC_REVISION: 81362306a36Sopenharmony_ci case SYSC_SYSCONFIG: 81462306a36Sopenharmony_ci case SYSC_SYSSTATUS: 81562306a36Sopenharmony_ci name = reg_names[reg]; 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci default: 81862306a36Sopenharmony_ci return -EINVAL; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci res = platform_get_resource_byname(to_platform_device(ddata->dev), 82262306a36Sopenharmony_ci IORESOURCE_MEM, name); 82362306a36Sopenharmony_ci if (!res) { 82462306a36Sopenharmony_ci ddata->offsets[reg] = -ENODEV; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci ddata->offsets[reg] = res->start - ddata->module_pa; 83062306a36Sopenharmony_ci if (reg == SYSC_REVISION) 83162306a36Sopenharmony_ci sysc_check_quirk_16bit(ddata, res); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int sysc_parse_registers(struct sysc *ddata) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int i, error; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < SYSC_MAX_REGS; i++) { 84162306a36Sopenharmony_ci error = sysc_parse_one(ddata, i); 84262306a36Sopenharmony_ci if (error) 84362306a36Sopenharmony_ci return error; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/** 85062306a36Sopenharmony_ci * sysc_check_registers - check for misconfigured register overlaps 85162306a36Sopenharmony_ci * @ddata: device driver data 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_cistatic int sysc_check_registers(struct sysc *ddata) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci int i, j, nr_regs = 0, nr_matches = 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci for (i = 0; i < SYSC_MAX_REGS; i++) { 85862306a36Sopenharmony_ci if (ddata->offsets[i] < 0) 85962306a36Sopenharmony_ci continue; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (ddata->offsets[i] > (ddata->module_size - 4)) { 86262306a36Sopenharmony_ci dev_err(ddata->dev, "register outside module range"); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return -EINVAL; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci for (j = 0; j < SYSC_MAX_REGS; j++) { 86862306a36Sopenharmony_ci if (ddata->offsets[j] < 0) 86962306a36Sopenharmony_ci continue; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (ddata->offsets[i] == ddata->offsets[j]) 87262306a36Sopenharmony_ci nr_matches++; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci nr_regs++; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (nr_matches > nr_regs) { 87862306a36Sopenharmony_ci dev_err(ddata->dev, "overlapping registers: (%i/%i)", 87962306a36Sopenharmony_ci nr_regs, nr_matches); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return -EINVAL; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return 0; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/** 88862306a36Sopenharmony_ci * sysc_ioremap - ioremap register space for the interconnect target module 88962306a36Sopenharmony_ci * @ddata: device driver data 89062306a36Sopenharmony_ci * 89162306a36Sopenharmony_ci * Note that the interconnect target module registers can be anywhere 89262306a36Sopenharmony_ci * within the interconnect target module range. For example, SGX has 89362306a36Sopenharmony_ci * them at offset 0x1fc00 in the 32MB module address space. And cpsw 89462306a36Sopenharmony_ci * has them at offset 0x1200 in the CPSW_WR child. Usually the 89562306a36Sopenharmony_ci * interconnect target module registers are at the beginning of 89662306a36Sopenharmony_ci * the module range though. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_cistatic int sysc_ioremap(struct sysc *ddata) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci int size; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (ddata->offsets[SYSC_REVISION] < 0 && 90362306a36Sopenharmony_ci ddata->offsets[SYSC_SYSCONFIG] < 0 && 90462306a36Sopenharmony_ci ddata->offsets[SYSC_SYSSTATUS] < 0) { 90562306a36Sopenharmony_ci size = ddata->module_size; 90662306a36Sopenharmony_ci } else { 90762306a36Sopenharmony_ci size = max3(ddata->offsets[SYSC_REVISION], 90862306a36Sopenharmony_ci ddata->offsets[SYSC_SYSCONFIG], 90962306a36Sopenharmony_ci ddata->offsets[SYSC_SYSSTATUS]); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (size < SZ_1K) 91262306a36Sopenharmony_ci size = SZ_1K; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if ((size + sizeof(u32)) > ddata->module_size) 91562306a36Sopenharmony_ci size = ddata->module_size; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ddata->module_va = devm_ioremap(ddata->dev, 91962306a36Sopenharmony_ci ddata->module_pa, 92062306a36Sopenharmony_ci size + sizeof(u32)); 92162306a36Sopenharmony_ci if (!ddata->module_va) 92262306a36Sopenharmony_ci return -EIO; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci/** 92862306a36Sopenharmony_ci * sysc_map_and_check_registers - ioremap and check device registers 92962306a36Sopenharmony_ci * @ddata: device driver data 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic int sysc_map_and_check_registers(struct sysc *ddata) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 93462306a36Sopenharmony_ci int error; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci error = sysc_parse_and_check_child_range(ddata); 93762306a36Sopenharmony_ci if (error) 93862306a36Sopenharmony_ci return error; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci error = sysc_defer_non_critical(ddata); 94162306a36Sopenharmony_ci if (error) 94262306a36Sopenharmony_ci return error; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci sysc_check_children(ddata); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (!of_property_present(np, "reg")) 94762306a36Sopenharmony_ci return 0; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci error = sysc_parse_registers(ddata); 95062306a36Sopenharmony_ci if (error) 95162306a36Sopenharmony_ci return error; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci error = sysc_ioremap(ddata); 95462306a36Sopenharmony_ci if (error) 95562306a36Sopenharmony_ci return error; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci error = sysc_check_registers(ddata); 95862306a36Sopenharmony_ci if (error) 95962306a36Sopenharmony_ci return error; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci return 0; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/** 96562306a36Sopenharmony_ci * sysc_show_rev - read and show interconnect target module revision 96662306a36Sopenharmony_ci * @bufp: buffer to print the information to 96762306a36Sopenharmony_ci * @ddata: device driver data 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cistatic int sysc_show_rev(char *bufp, struct sysc *ddata) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci int len; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (ddata->offsets[SYSC_REVISION] < 0) 97462306a36Sopenharmony_ci return sprintf(bufp, ":NA"); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci len = sprintf(bufp, ":%08x", ddata->revision); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return len; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic int sysc_show_reg(struct sysc *ddata, 98262306a36Sopenharmony_ci char *bufp, enum sysc_registers reg) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci if (ddata->offsets[reg] < 0) 98562306a36Sopenharmony_ci return sprintf(bufp, ":NA"); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return sprintf(bufp, ":%x", ddata->offsets[reg]); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int sysc_show_name(char *bufp, struct sysc *ddata) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci if (!ddata->name) 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return sprintf(bufp, ":%s", ddata->name); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/** 99962306a36Sopenharmony_ci * sysc_show_registers - show information about interconnect target module 100062306a36Sopenharmony_ci * @ddata: device driver data 100162306a36Sopenharmony_ci */ 100262306a36Sopenharmony_cistatic void sysc_show_registers(struct sysc *ddata) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci char buf[128]; 100562306a36Sopenharmony_ci char *bufp = buf; 100662306a36Sopenharmony_ci int i; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci for (i = 0; i < SYSC_MAX_REGS; i++) 100962306a36Sopenharmony_ci bufp += sysc_show_reg(ddata, bufp, i); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci bufp += sysc_show_rev(bufp, ddata); 101262306a36Sopenharmony_ci bufp += sysc_show_name(bufp, ddata); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci dev_dbg(ddata->dev, "%llx:%x%s\n", 101562306a36Sopenharmony_ci ddata->module_pa, ddata->module_size, 101662306a36Sopenharmony_ci buf); 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/** 102062306a36Sopenharmony_ci * sysc_write_sysconfig - handle sysconfig quirks for register write 102162306a36Sopenharmony_ci * @ddata: device driver data 102262306a36Sopenharmony_ci * @value: register value 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_cistatic void sysc_write_sysconfig(struct sysc *ddata, u32 value) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci if (ddata->module_unlock_quirk) 102762306a36Sopenharmony_ci ddata->module_unlock_quirk(ddata); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], value); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (ddata->module_lock_quirk) 103262306a36Sopenharmony_ci ddata->module_lock_quirk(ddata); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci#define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1) 103662306a36Sopenharmony_ci#define SYSC_CLOCACT_ICK 2 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */ 103962306a36Sopenharmony_cistatic int sysc_enable_module(struct device *dev) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct sysc *ddata; 104262306a36Sopenharmony_ci const struct sysc_regbits *regbits; 104362306a36Sopenharmony_ci u32 reg, idlemodes, best_mode; 104462306a36Sopenharmony_ci int error; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* 104962306a36Sopenharmony_ci * Some modules like DSS reset automatically on idle. Enable optional 105062306a36Sopenharmony_ci * reset clocks and wait for OCP softreset to complete. 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_IN_RESET) { 105362306a36Sopenharmony_ci error = sysc_enable_opt_clocks(ddata); 105462306a36Sopenharmony_ci if (error) { 105562306a36Sopenharmony_ci dev_err(ddata->dev, 105662306a36Sopenharmony_ci "Optional clocks failed for enable: %i\n", 105762306a36Sopenharmony_ci error); 105862306a36Sopenharmony_ci return error; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci /* 106262306a36Sopenharmony_ci * Some modules like i2c and hdq1w have unusable reset status unless 106362306a36Sopenharmony_ci * the module reset quirk is enabled. Skip status check on enable. 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ci if (!(ddata->cfg.quirks & SYSC_MODULE_QUIRK_ENA_RESETDONE)) { 106662306a36Sopenharmony_ci error = sysc_wait_softreset(ddata); 106762306a36Sopenharmony_ci if (error) 106862306a36Sopenharmony_ci dev_warn(ddata->dev, "OCP softreset timed out\n"); 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_IN_RESET) 107162306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * Some subsystem private interconnects, like DSS top level module, 107562306a36Sopenharmony_ci * need only the automatic OCP softreset handling with no sysconfig 107662306a36Sopenharmony_ci * register bits to configure. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci regbits = ddata->cap->regbits; 108262306a36Sopenharmony_ci reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* 108562306a36Sopenharmony_ci * Set CLOCKACTIVITY, we only use it for ick. And we only configure it 108662306a36Sopenharmony_ci * based on the SYSC_QUIRK_USE_CLOCKACT flag, not based on the hardware 108762306a36Sopenharmony_ci * capabilities. See the old HWMOD_SET_DEFAULT_CLOCKACT flag. 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci if (regbits->clkact_shift >= 0 && 109062306a36Sopenharmony_ci (ddata->cfg.quirks & SYSC_QUIRK_USE_CLOCKACT)) 109162306a36Sopenharmony_ci reg |= SYSC_CLOCACT_ICK << regbits->clkact_shift; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Set SIDLE mode */ 109462306a36Sopenharmony_ci idlemodes = ddata->cfg.sidlemodes; 109562306a36Sopenharmony_ci if (!idlemodes || regbits->sidle_shift < 0) 109662306a36Sopenharmony_ci goto set_midle; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_SIDLE | 109962306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT)) { 110062306a36Sopenharmony_ci best_mode = SYSC_IDLE_NO; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* Clear WAKEUP */ 110362306a36Sopenharmony_ci if (regbits->enwkup_shift >= 0 && 110462306a36Sopenharmony_ci ddata->cfg.sysc_val & BIT(regbits->enwkup_shift)) 110562306a36Sopenharmony_ci reg &= ~BIT(regbits->enwkup_shift); 110662306a36Sopenharmony_ci } else { 110762306a36Sopenharmony_ci best_mode = fls(ddata->cfg.sidlemodes) - 1; 110862306a36Sopenharmony_ci if (best_mode > SYSC_IDLE_MASK) { 110962306a36Sopenharmony_ci dev_err(dev, "%s: invalid sidlemode\n", __func__); 111062306a36Sopenharmony_ci return -EINVAL; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* Set WAKEUP */ 111462306a36Sopenharmony_ci if (regbits->enwkup_shift >= 0 && 111562306a36Sopenharmony_ci ddata->cfg.sysc_val & BIT(regbits->enwkup_shift)) 111662306a36Sopenharmony_ci reg |= BIT(regbits->enwkup_shift); 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); 112062306a36Sopenharmony_ci reg |= best_mode << regbits->sidle_shift; 112162306a36Sopenharmony_ci sysc_write_sysconfig(ddata, reg); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ciset_midle: 112462306a36Sopenharmony_ci /* Set MIDLE mode */ 112562306a36Sopenharmony_ci idlemodes = ddata->cfg.midlemodes; 112662306a36Sopenharmony_ci if (!idlemodes || regbits->midle_shift < 0) 112762306a36Sopenharmony_ci goto set_autoidle; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci best_mode = fls(ddata->cfg.midlemodes) - 1; 113062306a36Sopenharmony_ci if (best_mode > SYSC_IDLE_MASK) { 113162306a36Sopenharmony_ci dev_err(dev, "%s: invalid midlemode\n", __func__); 113262306a36Sopenharmony_ci error = -EINVAL; 113362306a36Sopenharmony_ci goto save_context; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) 113762306a36Sopenharmony_ci best_mode = SYSC_IDLE_NO; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); 114062306a36Sopenharmony_ci reg |= best_mode << regbits->midle_shift; 114162306a36Sopenharmony_ci sysc_write_sysconfig(ddata, reg); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ciset_autoidle: 114462306a36Sopenharmony_ci /* Autoidle bit must enabled separately if available */ 114562306a36Sopenharmony_ci if (regbits->autoidle_shift >= 0 && 114662306a36Sopenharmony_ci ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) { 114762306a36Sopenharmony_ci reg |= 1 << regbits->autoidle_shift; 114862306a36Sopenharmony_ci sysc_write_sysconfig(ddata, reg); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci error = 0; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cisave_context: 115462306a36Sopenharmony_ci /* Save context and flush posted write */ 115562306a36Sopenharmony_ci ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (ddata->module_enable_quirk) 115862306a36Sopenharmony_ci ddata->module_enable_quirk(ddata); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return error; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci if (idlemodes & BIT(SYSC_IDLE_SMART_WKUP)) 116662306a36Sopenharmony_ci *best_mode = SYSC_IDLE_SMART_WKUP; 116762306a36Sopenharmony_ci else if (idlemodes & BIT(SYSC_IDLE_SMART)) 116862306a36Sopenharmony_ci *best_mode = SYSC_IDLE_SMART; 116962306a36Sopenharmony_ci else if (idlemodes & BIT(SYSC_IDLE_FORCE)) 117062306a36Sopenharmony_ci *best_mode = SYSC_IDLE_FORCE; 117162306a36Sopenharmony_ci else 117262306a36Sopenharmony_ci return -EINVAL; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */ 117862306a36Sopenharmony_cistatic int sysc_disable_module(struct device *dev) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct sysc *ddata; 118162306a36Sopenharmony_ci const struct sysc_regbits *regbits; 118262306a36Sopenharmony_ci u32 reg, idlemodes, best_mode; 118362306a36Sopenharmony_ci int ret; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 118662306a36Sopenharmony_ci if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (ddata->module_disable_quirk) 119062306a36Sopenharmony_ci ddata->module_disable_quirk(ddata); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci regbits = ddata->cap->regbits; 119362306a36Sopenharmony_ci reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Set MIDLE mode */ 119662306a36Sopenharmony_ci idlemodes = ddata->cfg.midlemodes; 119762306a36Sopenharmony_ci if (!idlemodes || regbits->midle_shift < 0) 119862306a36Sopenharmony_ci goto set_sidle; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = sysc_best_idle_mode(idlemodes, &best_mode); 120162306a36Sopenharmony_ci if (ret) { 120262306a36Sopenharmony_ci dev_err(dev, "%s: invalid midlemode\n", __func__); 120362306a36Sopenharmony_ci return ret; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_MSTANDBY) || 120762306a36Sopenharmony_ci ddata->cfg.quirks & (SYSC_QUIRK_FORCE_MSTANDBY)) 120862306a36Sopenharmony_ci best_mode = SYSC_IDLE_FORCE; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); 121162306a36Sopenharmony_ci reg |= best_mode << regbits->midle_shift; 121262306a36Sopenharmony_ci sysc_write_sysconfig(ddata, reg); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ciset_sidle: 121562306a36Sopenharmony_ci /* Set SIDLE mode */ 121662306a36Sopenharmony_ci idlemodes = ddata->cfg.sidlemodes; 121762306a36Sopenharmony_ci if (!idlemodes || regbits->sidle_shift < 0) { 121862306a36Sopenharmony_ci ret = 0; 121962306a36Sopenharmony_ci goto save_context; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE) { 122362306a36Sopenharmony_ci best_mode = SYSC_IDLE_FORCE; 122462306a36Sopenharmony_ci } else { 122562306a36Sopenharmony_ci ret = sysc_best_idle_mode(idlemodes, &best_mode); 122662306a36Sopenharmony_ci if (ret) { 122762306a36Sopenharmony_ci dev_err(dev, "%s: invalid sidlemode\n", __func__); 122862306a36Sopenharmony_ci ret = -EINVAL; 122962306a36Sopenharmony_ci goto save_context; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) { 123462306a36Sopenharmony_ci /* Set WAKEUP */ 123562306a36Sopenharmony_ci if (regbits->enwkup_shift >= 0 && 123662306a36Sopenharmony_ci ddata->cfg.sysc_val & BIT(regbits->enwkup_shift)) 123762306a36Sopenharmony_ci reg |= BIT(regbits->enwkup_shift); 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); 124162306a36Sopenharmony_ci reg |= best_mode << regbits->sidle_shift; 124262306a36Sopenharmony_ci if (regbits->autoidle_shift >= 0 && 124362306a36Sopenharmony_ci ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) 124462306a36Sopenharmony_ci reg |= 1 << regbits->autoidle_shift; 124562306a36Sopenharmony_ci sysc_write_sysconfig(ddata, reg); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci ret = 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cisave_context: 125062306a36Sopenharmony_ci /* Save context and flush posted write */ 125162306a36Sopenharmony_ci ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return ret; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic int __maybe_unused sysc_runtime_suspend_legacy(struct device *dev, 125762306a36Sopenharmony_ci struct sysc *ddata) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata; 126062306a36Sopenharmony_ci int error; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci pdata = dev_get_platdata(ddata->dev); 126362306a36Sopenharmony_ci if (!pdata) 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (!pdata->idle_module) 126762306a36Sopenharmony_ci return -ENODEV; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci error = pdata->idle_module(dev, &ddata->cookie); 127062306a36Sopenharmony_ci if (error) 127162306a36Sopenharmony_ci dev_err(dev, "%s: could not idle: %i\n", 127262306a36Sopenharmony_ci __func__, error); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci reset_control_assert(ddata->rsts); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci return 0; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic int __maybe_unused sysc_runtime_resume_legacy(struct device *dev, 128062306a36Sopenharmony_ci struct sysc *ddata) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata; 128362306a36Sopenharmony_ci int error; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci pdata = dev_get_platdata(ddata->dev); 128662306a36Sopenharmony_ci if (!pdata) 128762306a36Sopenharmony_ci return 0; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!pdata->enable_module) 129062306a36Sopenharmony_ci return -ENODEV; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci error = pdata->enable_module(dev, &ddata->cookie); 129362306a36Sopenharmony_ci if (error) 129462306a36Sopenharmony_ci dev_err(dev, "%s: could not enable: %i\n", 129562306a36Sopenharmony_ci __func__, error); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci reset_control_deassert(ddata->rsts); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int __maybe_unused sysc_runtime_suspend(struct device *dev) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct sysc *ddata; 130562306a36Sopenharmony_ci int error = 0; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (!ddata->enabled) 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci sysc_clkdm_deny_idle(ddata); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (ddata->legacy_mode) { 131562306a36Sopenharmony_ci error = sysc_runtime_suspend_legacy(dev, ddata); 131662306a36Sopenharmony_ci if (error) 131762306a36Sopenharmony_ci goto err_allow_idle; 131862306a36Sopenharmony_ci } else { 131962306a36Sopenharmony_ci error = sysc_disable_module(dev); 132062306a36Sopenharmony_ci if (error) 132162306a36Sopenharmony_ci goto err_allow_idle; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci sysc_disable_main_clocks(ddata); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (sysc_opt_clks_needed(ddata)) 132762306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci ddata->enabled = false; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cierr_allow_idle: 133262306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci reset_control_assert(ddata->rsts); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci return error; 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic int __maybe_unused sysc_runtime_resume(struct device *dev) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct sysc *ddata; 134262306a36Sopenharmony_ci int error = 0; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (ddata->enabled) 134762306a36Sopenharmony_ci return 0; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci sysc_clkdm_deny_idle(ddata); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (sysc_opt_clks_needed(ddata)) { 135362306a36Sopenharmony_ci error = sysc_enable_opt_clocks(ddata); 135462306a36Sopenharmony_ci if (error) 135562306a36Sopenharmony_ci goto err_allow_idle; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci error = sysc_enable_main_clocks(ddata); 135962306a36Sopenharmony_ci if (error) 136062306a36Sopenharmony_ci goto err_opt_clocks; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci reset_control_deassert(ddata->rsts); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (ddata->legacy_mode) { 136562306a36Sopenharmony_ci error = sysc_runtime_resume_legacy(dev, ddata); 136662306a36Sopenharmony_ci if (error) 136762306a36Sopenharmony_ci goto err_main_clocks; 136862306a36Sopenharmony_ci } else { 136962306a36Sopenharmony_ci error = sysc_enable_module(dev); 137062306a36Sopenharmony_ci if (error) 137162306a36Sopenharmony_ci goto err_main_clocks; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci ddata->enabled = true; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci return 0; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cierr_main_clocks: 138162306a36Sopenharmony_ci sysc_disable_main_clocks(ddata); 138262306a36Sopenharmony_cierr_opt_clocks: 138362306a36Sopenharmony_ci if (sysc_opt_clks_needed(ddata)) 138462306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 138562306a36Sopenharmony_cierr_allow_idle: 138662306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci return error; 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci/* 139262306a36Sopenharmony_ci * Checks if device context was lost. Assumes the sysconfig register value 139362306a36Sopenharmony_ci * after lost context is different from the configured value. Only works for 139462306a36Sopenharmony_ci * enabled devices. 139562306a36Sopenharmony_ci * 139662306a36Sopenharmony_ci * Eventually we may want to also add support to using the context lost 139762306a36Sopenharmony_ci * registers that some SoCs have. 139862306a36Sopenharmony_ci */ 139962306a36Sopenharmony_cistatic int sysc_check_context(struct sysc *ddata) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci u32 reg; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (!ddata->enabled) 140462306a36Sopenharmony_ci return -ENODATA; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 140762306a36Sopenharmony_ci if (reg == ddata->sysconfig) 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return -EACCES; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic int sysc_reinit_module(struct sysc *ddata, bool leave_enabled) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct device *dev = ddata->dev; 141662306a36Sopenharmony_ci int error; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (ddata->enabled) { 141962306a36Sopenharmony_ci /* Nothing to do if enabled and context not lost */ 142062306a36Sopenharmony_ci error = sysc_check_context(ddata); 142162306a36Sopenharmony_ci if (!error) 142262306a36Sopenharmony_ci return 0; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* Disable target module if it is enabled */ 142562306a36Sopenharmony_ci error = sysc_runtime_suspend(dev); 142662306a36Sopenharmony_ci if (error) 142762306a36Sopenharmony_ci dev_warn(dev, "reinit suspend failed: %i\n", error); 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* Enable target module */ 143162306a36Sopenharmony_ci error = sysc_runtime_resume(dev); 143262306a36Sopenharmony_ci if (error) 143362306a36Sopenharmony_ci dev_warn(dev, "reinit resume failed: %i\n", error); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* Some modules like am335x gpmc need reset and restore of sysconfig */ 143662306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_RESET_ON_CTX_LOST) { 143762306a36Sopenharmony_ci error = sysc_reset(ddata); 143862306a36Sopenharmony_ci if (error) 143962306a36Sopenharmony_ci dev_warn(dev, "reinit reset failed: %i\n", error); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci sysc_write_sysconfig(ddata, ddata->sysconfig); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (leave_enabled) 144562306a36Sopenharmony_ci return error; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci /* Disable target module if no leave_enabled was set */ 144862306a36Sopenharmony_ci error = sysc_runtime_suspend(dev); 144962306a36Sopenharmony_ci if (error) 145062306a36Sopenharmony_ci dev_warn(dev, "reinit suspend failed: %i\n", error); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci return error; 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic int __maybe_unused sysc_noirq_suspend(struct device *dev) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct sysc *ddata; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (ddata->cfg.quirks & 146262306a36Sopenharmony_ci (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE)) 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (!ddata->enabled) 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci ddata->needs_resume = 1; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci return sysc_runtime_suspend(dev); 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int __maybe_unused sysc_noirq_resume(struct device *dev) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct sysc *ddata; 147662306a36Sopenharmony_ci int error = 0; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci ddata = dev_get_drvdata(dev); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (ddata->cfg.quirks & 148162306a36Sopenharmony_ci (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE)) 148262306a36Sopenharmony_ci return 0; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_RESUME) { 148562306a36Sopenharmony_ci error = sysc_reinit_module(ddata, ddata->needs_resume); 148662306a36Sopenharmony_ci if (error) 148762306a36Sopenharmony_ci dev_warn(dev, "noirq_resume failed: %i\n", error); 148862306a36Sopenharmony_ci } else if (ddata->needs_resume) { 148962306a36Sopenharmony_ci error = sysc_runtime_resume(dev); 149062306a36Sopenharmony_ci if (error) 149162306a36Sopenharmony_ci dev_warn(dev, "noirq_resume failed: %i\n", error); 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci ddata->needs_resume = 0; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci return error; 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic const struct dev_pm_ops sysc_pm_ops = { 150062306a36Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_noirq_suspend, sysc_noirq_resume) 150162306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sysc_runtime_suspend, 150262306a36Sopenharmony_ci sysc_runtime_resume, 150362306a36Sopenharmony_ci NULL) 150462306a36Sopenharmony_ci}; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci/* Module revision register based quirks */ 150762306a36Sopenharmony_cistruct sysc_revision_quirk { 150862306a36Sopenharmony_ci const char *name; 150962306a36Sopenharmony_ci u32 base; 151062306a36Sopenharmony_ci int rev_offset; 151162306a36Sopenharmony_ci int sysc_offset; 151262306a36Sopenharmony_ci int syss_offset; 151362306a36Sopenharmony_ci u32 revision; 151462306a36Sopenharmony_ci u32 revision_mask; 151562306a36Sopenharmony_ci u32 quirks; 151662306a36Sopenharmony_ci}; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci#define SYSC_QUIRK(optname, optbase, optrev, optsysc, optsyss, \ 151962306a36Sopenharmony_ci optrev_val, optrevmask, optquirkmask) \ 152062306a36Sopenharmony_ci { \ 152162306a36Sopenharmony_ci .name = (optname), \ 152262306a36Sopenharmony_ci .base = (optbase), \ 152362306a36Sopenharmony_ci .rev_offset = (optrev), \ 152462306a36Sopenharmony_ci .sysc_offset = (optsysc), \ 152562306a36Sopenharmony_ci .syss_offset = (optsyss), \ 152662306a36Sopenharmony_ci .revision = (optrev_val), \ 152762306a36Sopenharmony_ci .revision_mask = (optrevmask), \ 152862306a36Sopenharmony_ci .quirks = (optquirkmask), \ 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic const struct sysc_revision_quirk sysc_revision_quirks[] = { 153262306a36Sopenharmony_ci /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ 153362306a36Sopenharmony_ci SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff, 153462306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), 153562306a36Sopenharmony_ci SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, 153662306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), 153762306a36Sopenharmony_ci /* Uarts on omap4 and later */ 153862306a36Sopenharmony_ci SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff, 153962306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), 154062306a36Sopenharmony_ci SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff, 154162306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), 154262306a36Sopenharmony_ci SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff, 154362306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* Quirks that need to be set based on the module address */ 154662306a36Sopenharmony_ci SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff, 154762306a36Sopenharmony_ci SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT | 154862306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE), 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* Quirks that need to be set based on detected module */ 155162306a36Sopenharmony_ci SYSC_QUIRK("aess", 0, 0, 0x10, -ENODEV, 0x40000000, 0xffffffff, 155262306a36Sopenharmony_ci SYSC_MODULE_QUIRK_AESS), 155362306a36Sopenharmony_ci /* Errata i893 handling for dra7 dcan1 and 2 */ 155462306a36Sopenharmony_ci SYSC_QUIRK("dcan", 0x4ae3c000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, 155562306a36Sopenharmony_ci SYSC_QUIRK_CLKDM_NOAUTO), 155662306a36Sopenharmony_ci SYSC_QUIRK("dcan", 0x48480000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, 155762306a36Sopenharmony_ci SYSC_QUIRK_CLKDM_NOAUTO), 155862306a36Sopenharmony_ci SYSC_QUIRK("dss", 0x4832a000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, 155962306a36Sopenharmony_ci SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), 156062306a36Sopenharmony_ci SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000040, 0xffffffff, 156162306a36Sopenharmony_ci SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), 156262306a36Sopenharmony_ci SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000061, 0xffffffff, 156362306a36Sopenharmony_ci SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), 156462306a36Sopenharmony_ci SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, 156562306a36Sopenharmony_ci SYSC_QUIRK_CLKDM_NOAUTO), 156662306a36Sopenharmony_ci SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, 156762306a36Sopenharmony_ci SYSC_QUIRK_CLKDM_NOAUTO), 156862306a36Sopenharmony_ci SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff, 156962306a36Sopenharmony_ci SYSC_QUIRK_OPT_CLKS_IN_RESET), 157062306a36Sopenharmony_ci SYSC_QUIRK("gpmc", 0, 0, 0x10, 0x14, 0x00000060, 0xffffffff, 157162306a36Sopenharmony_ci SYSC_QUIRK_REINIT_ON_CTX_LOST | SYSC_QUIRK_RESET_ON_CTX_LOST | 157262306a36Sopenharmony_ci SYSC_QUIRK_GPMC_DEBUG), 157362306a36Sopenharmony_ci SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50030200, 0xffffffff, 157462306a36Sopenharmony_ci SYSC_QUIRK_OPT_CLKS_NEEDED), 157562306a36Sopenharmony_ci SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 157662306a36Sopenharmony_ci SYSC_MODULE_QUIRK_HDQ1W | SYSC_MODULE_QUIRK_ENA_RESETDONE), 157762306a36Sopenharmony_ci SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, 157862306a36Sopenharmony_ci SYSC_MODULE_QUIRK_HDQ1W | SYSC_MODULE_QUIRK_ENA_RESETDONE), 157962306a36Sopenharmony_ci SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000036, 0x000000ff, 158062306a36Sopenharmony_ci SYSC_MODULE_QUIRK_I2C | SYSC_MODULE_QUIRK_ENA_RESETDONE), 158162306a36Sopenharmony_ci SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x0000003c, 0x000000ff, 158262306a36Sopenharmony_ci SYSC_MODULE_QUIRK_I2C | SYSC_MODULE_QUIRK_ENA_RESETDONE), 158362306a36Sopenharmony_ci SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000040, 0x000000ff, 158462306a36Sopenharmony_ci SYSC_MODULE_QUIRK_I2C | SYSC_MODULE_QUIRK_ENA_RESETDONE), 158562306a36Sopenharmony_ci SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, 158662306a36Sopenharmony_ci SYSC_MODULE_QUIRK_I2C | SYSC_MODULE_QUIRK_ENA_RESETDONE), 158762306a36Sopenharmony_ci SYSC_QUIRK("gpu", 0x50000000, 0x14, -ENODEV, -ENODEV, 0x00010201, 0xffffffff, 0), 158862306a36Sopenharmony_ci SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -ENODEV, 0x40000000 , 0xffffffff, 158962306a36Sopenharmony_ci SYSC_MODULE_QUIRK_SGX), 159062306a36Sopenharmony_ci SYSC_QUIRK("lcdc", 0, 0, 0x54, -ENODEV, 0x4f201000, 0xffffffff, 159162306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 159262306a36Sopenharmony_ci SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff, 159362306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE), 159462306a36Sopenharmony_ci SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0, 159562306a36Sopenharmony_ci SYSC_MODULE_QUIRK_RTC_UNLOCK), 159662306a36Sopenharmony_ci SYSC_QUIRK("tptc", 0, 0, 0x10, -ENODEV, 0x40006c00, 0xffffefff, 159762306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 159862306a36Sopenharmony_ci SYSC_QUIRK("tptc", 0, 0, -ENODEV, -ENODEV, 0x40007c00, 0xffffffff, 159962306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 160062306a36Sopenharmony_ci SYSC_QUIRK("sata", 0, 0xfc, 0x1100, -ENODEV, 0x5e412000, 0xffffffff, 160162306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 160262306a36Sopenharmony_ci SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 160362306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 160462306a36Sopenharmony_ci SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff, 160562306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), 160662306a36Sopenharmony_ci SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000033, 160762306a36Sopenharmony_ci 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY | 160862306a36Sopenharmony_ci SYSC_MODULE_QUIRK_OTG), 160962306a36Sopenharmony_ci SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000040, 161062306a36Sopenharmony_ci 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY | 161162306a36Sopenharmony_ci SYSC_MODULE_QUIRK_OTG), 161262306a36Sopenharmony_ci SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, 161362306a36Sopenharmony_ci 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY | 161462306a36Sopenharmony_ci SYSC_MODULE_QUIRK_OTG), 161562306a36Sopenharmony_ci SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -ENODEV, 0x4ea2080d, 0xffffffff, 161662306a36Sopenharmony_ci SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY | 161762306a36Sopenharmony_ci SYSC_QUIRK_REINIT_ON_CTX_LOST), 161862306a36Sopenharmony_ci SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 161962306a36Sopenharmony_ci SYSC_MODULE_QUIRK_WDT), 162062306a36Sopenharmony_ci /* PRUSS on am3, am4 and am5 */ 162162306a36Sopenharmony_ci SYSC_QUIRK("pruss", 0, 0x26000, 0x26004, -ENODEV, 0x47000000, 0xff000000, 162262306a36Sopenharmony_ci SYSC_MODULE_QUIRK_PRUSS), 162362306a36Sopenharmony_ci /* Watchdog on am3 and am4 */ 162462306a36Sopenharmony_ci SYSC_QUIRK("wdt", 0x44e35000, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 162562306a36Sopenharmony_ci SYSC_MODULE_QUIRK_WDT | SYSC_QUIRK_SWSUP_SIDLE), 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci#ifdef DEBUG 162862306a36Sopenharmony_ci SYSC_QUIRK("adc", 0, 0, 0x10, -ENODEV, 0x47300001, 0xffffffff, 0), 162962306a36Sopenharmony_ci SYSC_QUIRK("atl", 0, 0, -ENODEV, -ENODEV, 0x0a070100, 0xffffffff, 0), 163062306a36Sopenharmony_ci SYSC_QUIRK("cm", 0, 0, -ENODEV, -ENODEV, 0x40000301, 0xffffffff, 0), 163162306a36Sopenharmony_ci SYSC_QUIRK("control", 0, 0, 0x10, -ENODEV, 0x40000900, 0xffffffff, 0), 163262306a36Sopenharmony_ci SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902, 163362306a36Sopenharmony_ci 0xffff00f0, 0), 163462306a36Sopenharmony_ci SYSC_QUIRK("dcan", 0, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, 0), 163562306a36Sopenharmony_ci SYSC_QUIRK("dcan", 0, 0x20, -ENODEV, -ENODEV, 0x4edb1902, 0xffffffff, 0), 163662306a36Sopenharmony_ci SYSC_QUIRK("dispc", 0x4832a400, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), 163762306a36Sopenharmony_ci SYSC_QUIRK("dispc", 0x58001000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), 163862306a36Sopenharmony_ci SYSC_QUIRK("dispc", 0x58001000, 0, 0x10, 0x14, 0x00000051, 0xffffffff, 0), 163962306a36Sopenharmony_ci SYSC_QUIRK("dmic", 0, 0, 0x10, -ENODEV, 0x50010000, 0xffffffff, 0), 164062306a36Sopenharmony_ci SYSC_QUIRK("dsi", 0x58004000, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), 164162306a36Sopenharmony_ci SYSC_QUIRK("dsi", 0x58005000, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), 164262306a36Sopenharmony_ci SYSC_QUIRK("dsi", 0x58005000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), 164362306a36Sopenharmony_ci SYSC_QUIRK("dsi", 0x58009000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), 164462306a36Sopenharmony_ci SYSC_QUIRK("dwc3", 0, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, 0), 164562306a36Sopenharmony_ci SYSC_QUIRK("d2d", 0x4a0b6000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), 164662306a36Sopenharmony_ci SYSC_QUIRK("d2d", 0x4a0cd000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), 164762306a36Sopenharmony_ci SYSC_QUIRK("elm", 0x48080000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, 0), 164862306a36Sopenharmony_ci SYSC_QUIRK("emif", 0, 0, -ENODEV, -ENODEV, 0x40441403, 0xffff0fff, 0), 164962306a36Sopenharmony_ci SYSC_QUIRK("emif", 0, 0, -ENODEV, -ENODEV, 0x50440500, 0xffffffff, 0), 165062306a36Sopenharmony_ci SYSC_QUIRK("epwmss", 0, 0, 0x4, -ENODEV, 0x47400001, 0xffffffff, 0), 165162306a36Sopenharmony_ci SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -ENODEV, 0, 0, 0), 165262306a36Sopenharmony_ci SYSC_QUIRK("gpu", 0, 0xfe00, 0xfe10, -ENODEV, 0x40000000 , 0xffffffff, 0), 165362306a36Sopenharmony_ci SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50031d00, 0xffffffff, 0), 165462306a36Sopenharmony_ci SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0), 165562306a36Sopenharmony_ci SYSC_QUIRK("iss", 0, 0, 0x10, -ENODEV, 0x40000101, 0xffffffff, 0), 165662306a36Sopenharmony_ci SYSC_QUIRK("keypad", 0x4a31c000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, 0), 165762306a36Sopenharmony_ci SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44307b02, 0xffffffff, 0), 165862306a36Sopenharmony_ci SYSC_QUIRK("mcbsp", 0, -ENODEV, 0x8c, -ENODEV, 0, 0, 0), 165962306a36Sopenharmony_ci SYSC_QUIRK("mcspi", 0, 0, 0x10, -ENODEV, 0x40300a0b, 0xffff00ff, 0), 166062306a36Sopenharmony_ci SYSC_QUIRK("mcspi", 0, 0, 0x110, 0x114, 0x40300a0b, 0xffffffff, 0), 166162306a36Sopenharmony_ci SYSC_QUIRK("mailbox", 0, 0, 0x10, -ENODEV, 0x00000400, 0xffffffff, 0), 166262306a36Sopenharmony_ci SYSC_QUIRK("m3", 0, 0, -ENODEV, -ENODEV, 0x5f580105, 0x0fff0f00, 0), 166362306a36Sopenharmony_ci SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xfffffff0, 0), 166462306a36Sopenharmony_ci SYSC_QUIRK("ocp2scp", 0, 0, -ENODEV, -ENODEV, 0x50060007, 0xffffffff, 0), 166562306a36Sopenharmony_ci SYSC_QUIRK("padconf", 0, 0, 0x10, -ENODEV, 0x4fff0800, 0xffffffff, 0), 166662306a36Sopenharmony_ci SYSC_QUIRK("padconf", 0, 0, -ENODEV, -ENODEV, 0x40001100, 0xffffffff, 0), 166762306a36Sopenharmony_ci SYSC_QUIRK("pcie", 0x51000000, -ENODEV, -ENODEV, -ENODEV, 0, 0, 0), 166862306a36Sopenharmony_ci SYSC_QUIRK("pcie", 0x51800000, -ENODEV, -ENODEV, -ENODEV, 0, 0, 0), 166962306a36Sopenharmony_ci SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x40000100, 0xffffffff, 0), 167062306a36Sopenharmony_ci SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x00004102, 0xffffffff, 0), 167162306a36Sopenharmony_ci SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x40000400, 0xffffffff, 0), 167262306a36Sopenharmony_ci SYSC_QUIRK("rfbi", 0x4832a800, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), 167362306a36Sopenharmony_ci SYSC_QUIRK("rfbi", 0x58002000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), 167462306a36Sopenharmony_ci SYSC_QUIRK("scm", 0, 0, 0x10, -ENODEV, 0x40000900, 0xffffffff, 0), 167562306a36Sopenharmony_ci SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x4e8b0100, 0xffffffff, 0), 167662306a36Sopenharmony_ci SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x4f000100, 0xffffffff, 0), 167762306a36Sopenharmony_ci SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x40000900, 0xffffffff, 0), 167862306a36Sopenharmony_ci SYSC_QUIRK("scrm", 0, 0, -ENODEV, -ENODEV, 0x00000010, 0xffffffff, 0), 167962306a36Sopenharmony_ci SYSC_QUIRK("sdio", 0, 0, 0x10, -ENODEV, 0x40202301, 0xffff0ff0, 0), 168062306a36Sopenharmony_ci SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0), 168162306a36Sopenharmony_ci SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0), 168262306a36Sopenharmony_ci SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff, 0), 168362306a36Sopenharmony_ci SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0), 168462306a36Sopenharmony_ci SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0), 168562306a36Sopenharmony_ci SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, 0), 168662306a36Sopenharmony_ci SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff, 0), 168762306a36Sopenharmony_ci SYSC_QUIRK("spinlock", 0, 0, 0x10, -ENODEV, 0x50020000, 0xffffffff, 0), 168862306a36Sopenharmony_ci SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -ENODEV, 0x00000020, 0xffffffff, 0), 168962306a36Sopenharmony_ci SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000013, 0xffffffff, 0), 169062306a36Sopenharmony_ci SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff, 0), 169162306a36Sopenharmony_ci /* Some timers on omap4 and later */ 169262306a36Sopenharmony_ci SYSC_QUIRK("timer", 0, 0, 0x10, -ENODEV, 0x50002100, 0xffffffff, 0), 169362306a36Sopenharmony_ci SYSC_QUIRK("timer", 0, 0, 0x10, -ENODEV, 0x4fff1301, 0xffff00ff, 0), 169462306a36Sopenharmony_ci SYSC_QUIRK("timer32k", 0, 0, 0x4, -ENODEV, 0x00000040, 0xffffffff, 0), 169562306a36Sopenharmony_ci SYSC_QUIRK("timer32k", 0, 0, 0x4, -ENODEV, 0x00000011, 0xffffffff, 0), 169662306a36Sopenharmony_ci SYSC_QUIRK("timer32k", 0, 0, 0x4, -ENODEV, 0x00000060, 0xffffffff, 0), 169762306a36Sopenharmony_ci SYSC_QUIRK("tpcc", 0, 0, -ENODEV, -ENODEV, 0x40014c00, 0xffffffff, 0), 169862306a36Sopenharmony_ci SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0), 169962306a36Sopenharmony_ci SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), 170062306a36Sopenharmony_ci SYSC_QUIRK("venc", 0x58003000, 0, -ENODEV, -ENODEV, 0x00000002, 0xffffffff, 0), 170162306a36Sopenharmony_ci SYSC_QUIRK("vfpe", 0, 0, 0x104, -ENODEV, 0x4d001200, 0xffffffff, 0), 170262306a36Sopenharmony_ci#endif 170362306a36Sopenharmony_ci}; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci/* 170662306a36Sopenharmony_ci * Early quirks based on module base and register offsets only that are 170762306a36Sopenharmony_ci * needed before the module revision can be read 170862306a36Sopenharmony_ci */ 170962306a36Sopenharmony_cistatic void sysc_init_early_quirks(struct sysc *ddata) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci const struct sysc_revision_quirk *q; 171262306a36Sopenharmony_ci int i; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sysc_revision_quirks); i++) { 171562306a36Sopenharmony_ci q = &sysc_revision_quirks[i]; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (!q->base) 171862306a36Sopenharmony_ci continue; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (q->base != ddata->module_pa) 172162306a36Sopenharmony_ci continue; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (q->rev_offset != ddata->offsets[SYSC_REVISION]) 172462306a36Sopenharmony_ci continue; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) 172762306a36Sopenharmony_ci continue; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) 173062306a36Sopenharmony_ci continue; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci ddata->name = q->name; 173362306a36Sopenharmony_ci ddata->cfg.quirks |= q->quirks; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci/* Quirks that also consider the revision register value */ 173862306a36Sopenharmony_cistatic void sysc_init_revision_quirks(struct sysc *ddata) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci const struct sysc_revision_quirk *q; 174162306a36Sopenharmony_ci int i; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sysc_revision_quirks); i++) { 174462306a36Sopenharmony_ci q = &sysc_revision_quirks[i]; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci if (q->base && q->base != ddata->module_pa) 174762306a36Sopenharmony_ci continue; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci if (q->rev_offset != ddata->offsets[SYSC_REVISION]) 175062306a36Sopenharmony_ci continue; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci if (q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) 175362306a36Sopenharmony_ci continue; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) 175662306a36Sopenharmony_ci continue; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (q->revision == ddata->revision || 175962306a36Sopenharmony_ci (q->revision & q->revision_mask) == 176062306a36Sopenharmony_ci (ddata->revision & q->revision_mask)) { 176162306a36Sopenharmony_ci ddata->name = q->name; 176262306a36Sopenharmony_ci ddata->cfg.quirks |= q->quirks; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci/* 176862306a36Sopenharmony_ci * DSS needs dispc outputs disabled to reset modules. Returns mask of 176962306a36Sopenharmony_ci * enabled DSS interrupts. Eventually we may be able to do this on 177062306a36Sopenharmony_ci * dispc init rather than top-level DSS init. 177162306a36Sopenharmony_ci */ 177262306a36Sopenharmony_cistatic u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset, 177362306a36Sopenharmony_ci bool disable) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; 177662306a36Sopenharmony_ci const int lcd_en_mask = BIT(0), digit_en_mask = BIT(1); 177762306a36Sopenharmony_ci int manager_count; 177862306a36Sopenharmony_ci bool framedonetv_irq = true; 177962306a36Sopenharmony_ci u32 val, irq_mask = 0; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci switch (sysc_soc->soc) { 178262306a36Sopenharmony_ci case SOC_2420 ... SOC_3630: 178362306a36Sopenharmony_ci manager_count = 2; 178462306a36Sopenharmony_ci framedonetv_irq = false; 178562306a36Sopenharmony_ci break; 178662306a36Sopenharmony_ci case SOC_4430 ... SOC_4470: 178762306a36Sopenharmony_ci manager_count = 3; 178862306a36Sopenharmony_ci break; 178962306a36Sopenharmony_ci case SOC_5430: 179062306a36Sopenharmony_ci case SOC_DRA7: 179162306a36Sopenharmony_ci manager_count = 4; 179262306a36Sopenharmony_ci break; 179362306a36Sopenharmony_ci case SOC_AM4: 179462306a36Sopenharmony_ci manager_count = 1; 179562306a36Sopenharmony_ci framedonetv_irq = false; 179662306a36Sopenharmony_ci break; 179762306a36Sopenharmony_ci case SOC_UNKNOWN: 179862306a36Sopenharmony_ci default: 179962306a36Sopenharmony_ci return 0; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci /* Remap the whole module range to be able to reset dispc outputs */ 180362306a36Sopenharmony_ci devm_iounmap(ddata->dev, ddata->module_va); 180462306a36Sopenharmony_ci ddata->module_va = devm_ioremap(ddata->dev, 180562306a36Sopenharmony_ci ddata->module_pa, 180662306a36Sopenharmony_ci ddata->module_size); 180762306a36Sopenharmony_ci if (!ddata->module_va) 180862306a36Sopenharmony_ci return -EIO; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci /* DISP_CONTROL, shut down lcd and digit on disable if enabled */ 181162306a36Sopenharmony_ci val = sysc_read(ddata, dispc_offset + 0x40); 181262306a36Sopenharmony_ci lcd_en = val & lcd_en_mask; 181362306a36Sopenharmony_ci digit_en = val & digit_en_mask; 181462306a36Sopenharmony_ci if (lcd_en) 181562306a36Sopenharmony_ci irq_mask |= BIT(0); /* FRAMEDONE */ 181662306a36Sopenharmony_ci if (digit_en) { 181762306a36Sopenharmony_ci if (framedonetv_irq) 181862306a36Sopenharmony_ci irq_mask |= BIT(24); /* FRAMEDONETV */ 181962306a36Sopenharmony_ci else 182062306a36Sopenharmony_ci irq_mask |= BIT(2) | BIT(3); /* EVSYNC bits */ 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci if (disable && (lcd_en || digit_en)) 182362306a36Sopenharmony_ci sysc_write(ddata, dispc_offset + 0x40, 182462306a36Sopenharmony_ci val & ~(lcd_en_mask | digit_en_mask)); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (manager_count <= 2) 182762306a36Sopenharmony_ci return irq_mask; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci /* DISPC_CONTROL2 */ 183062306a36Sopenharmony_ci val = sysc_read(ddata, dispc_offset + 0x238); 183162306a36Sopenharmony_ci lcd2_en = val & lcd_en_mask; 183262306a36Sopenharmony_ci if (lcd2_en) 183362306a36Sopenharmony_ci irq_mask |= BIT(22); /* FRAMEDONE2 */ 183462306a36Sopenharmony_ci if (disable && lcd2_en) 183562306a36Sopenharmony_ci sysc_write(ddata, dispc_offset + 0x238, 183662306a36Sopenharmony_ci val & ~lcd_en_mask); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (manager_count <= 3) 183962306a36Sopenharmony_ci return irq_mask; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* DISPC_CONTROL3 */ 184262306a36Sopenharmony_ci val = sysc_read(ddata, dispc_offset + 0x848); 184362306a36Sopenharmony_ci lcd3_en = val & lcd_en_mask; 184462306a36Sopenharmony_ci if (lcd3_en) 184562306a36Sopenharmony_ci irq_mask |= BIT(30); /* FRAMEDONE3 */ 184662306a36Sopenharmony_ci if (disable && lcd3_en) 184762306a36Sopenharmony_ci sysc_write(ddata, dispc_offset + 0x848, 184862306a36Sopenharmony_ci val & ~lcd_en_mask); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci return irq_mask; 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci/* DSS needs child outputs disabled and SDI registers cleared for reset */ 185462306a36Sopenharmony_cistatic void sysc_pre_reset_quirk_dss(struct sysc *ddata) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci const int dispc_offset = 0x1000; 185762306a36Sopenharmony_ci int error; 185862306a36Sopenharmony_ci u32 irq_mask, val; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci /* Get enabled outputs */ 186162306a36Sopenharmony_ci irq_mask = sysc_quirk_dispc(ddata, dispc_offset, false); 186262306a36Sopenharmony_ci if (!irq_mask) 186362306a36Sopenharmony_ci return; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci /* Clear IRQSTATUS */ 186662306a36Sopenharmony_ci sysc_write(ddata, dispc_offset + 0x18, irq_mask); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* Disable outputs */ 186962306a36Sopenharmony_ci val = sysc_quirk_dispc(ddata, dispc_offset, true); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* Poll IRQSTATUS */ 187262306a36Sopenharmony_ci error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18, 187362306a36Sopenharmony_ci val, val != irq_mask, 100, 50); 187462306a36Sopenharmony_ci if (error) 187562306a36Sopenharmony_ci dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n", 187662306a36Sopenharmony_ci __func__, val, irq_mask); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if (sysc_soc->soc == SOC_3430 || sysc_soc->soc == SOC_AM35) { 187962306a36Sopenharmony_ci /* Clear DSS_SDI_CONTROL */ 188062306a36Sopenharmony_ci sysc_write(ddata, 0x44, 0); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci /* Clear DSS_PLL_CONTROL */ 188362306a36Sopenharmony_ci sysc_write(ddata, 0x48, 0); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */ 188762306a36Sopenharmony_ci sysc_write(ddata, 0x40, 0); 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci/* 1-wire needs module's internal clocks enabled for reset */ 189162306a36Sopenharmony_cistatic void sysc_pre_reset_quirk_hdq1w(struct sysc *ddata) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci int offset = 0x0c; /* HDQ_CTRL_STATUS */ 189462306a36Sopenharmony_ci u16 val; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci val = sysc_read(ddata, offset); 189762306a36Sopenharmony_ci val |= BIT(5); 189862306a36Sopenharmony_ci sysc_write(ddata, offset, val); 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci/* AESS (Audio Engine SubSystem) needs autogating set after enable */ 190262306a36Sopenharmony_cistatic void sysc_module_enable_quirk_aess(struct sysc *ddata) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci int offset = 0x7c; /* AESS_AUTO_GATING_ENABLE */ 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci sysc_write(ddata, offset, 1); 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci/* I2C needs to be disabled for reset */ 191062306a36Sopenharmony_cistatic void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci int offset; 191362306a36Sopenharmony_ci u16 val; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* I2C_CON, omap2/3 is different from omap4 and later */ 191662306a36Sopenharmony_ci if ((ddata->revision & 0xffffff00) == 0x001f0000) 191762306a36Sopenharmony_ci offset = 0x24; 191862306a36Sopenharmony_ci else 191962306a36Sopenharmony_ci offset = 0xa4; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* I2C_EN */ 192262306a36Sopenharmony_ci val = sysc_read(ddata, offset); 192362306a36Sopenharmony_ci if (enable) 192462306a36Sopenharmony_ci val |= BIT(15); 192562306a36Sopenharmony_ci else 192662306a36Sopenharmony_ci val &= ~BIT(15); 192762306a36Sopenharmony_ci sysc_write(ddata, offset, val); 192862306a36Sopenharmony_ci} 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_cistatic void sysc_pre_reset_quirk_i2c(struct sysc *ddata) 193162306a36Sopenharmony_ci{ 193262306a36Sopenharmony_ci sysc_clk_quirk_i2c(ddata, false); 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_cistatic void sysc_post_reset_quirk_i2c(struct sysc *ddata) 193662306a36Sopenharmony_ci{ 193762306a36Sopenharmony_ci sysc_clk_quirk_i2c(ddata, true); 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci/* RTC on am3 and 4 needs to be unlocked and locked for sysconfig */ 194162306a36Sopenharmony_cistatic void sysc_quirk_rtc(struct sysc *ddata, bool lock) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci u32 val, kick0_val = 0, kick1_val = 0; 194462306a36Sopenharmony_ci unsigned long flags; 194562306a36Sopenharmony_ci int error; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (!lock) { 194862306a36Sopenharmony_ci kick0_val = 0x83e70b13; 194962306a36Sopenharmony_ci kick1_val = 0x95a4f1e0; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci local_irq_save(flags); 195362306a36Sopenharmony_ci /* RTC_STATUS BUSY bit may stay active for 1/32768 seconds (~30 usec) */ 195462306a36Sopenharmony_ci error = readl_poll_timeout_atomic(ddata->module_va + 0x44, val, 195562306a36Sopenharmony_ci !(val & BIT(0)), 100, 50); 195662306a36Sopenharmony_ci if (error) 195762306a36Sopenharmony_ci dev_warn(ddata->dev, "rtc busy timeout\n"); 195862306a36Sopenharmony_ci /* Now we have ~15 microseconds to read/write various registers */ 195962306a36Sopenharmony_ci sysc_write(ddata, 0x6c, kick0_val); 196062306a36Sopenharmony_ci sysc_write(ddata, 0x70, kick1_val); 196162306a36Sopenharmony_ci local_irq_restore(flags); 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_cistatic void sysc_module_unlock_quirk_rtc(struct sysc *ddata) 196562306a36Sopenharmony_ci{ 196662306a36Sopenharmony_ci sysc_quirk_rtc(ddata, false); 196762306a36Sopenharmony_ci} 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_cistatic void sysc_module_lock_quirk_rtc(struct sysc *ddata) 197062306a36Sopenharmony_ci{ 197162306a36Sopenharmony_ci sysc_quirk_rtc(ddata, true); 197262306a36Sopenharmony_ci} 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci/* OTG omap2430 glue layer up to omap4 needs OTG_FORCESTDBY configured */ 197562306a36Sopenharmony_cistatic void sysc_module_enable_quirk_otg(struct sysc *ddata) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci int offset = 0x414; /* OTG_FORCESTDBY */ 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci sysc_write(ddata, offset, 0); 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic void sysc_module_disable_quirk_otg(struct sysc *ddata) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci int offset = 0x414; /* OTG_FORCESTDBY */ 198562306a36Sopenharmony_ci u32 val = BIT(0); /* ENABLEFORCE */ 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci sysc_write(ddata, offset, val); 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci/* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */ 199162306a36Sopenharmony_cistatic void sysc_module_enable_quirk_sgx(struct sysc *ddata) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci int offset = 0xff08; /* OCP_DEBUG_CONFIG */ 199462306a36Sopenharmony_ci u32 val = BIT(31); /* THALIA_INT_BYPASS */ 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci sysc_write(ddata, offset, val); 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci/* Watchdog timer needs a disable sequence after reset */ 200062306a36Sopenharmony_cistatic void sysc_reset_done_quirk_wdt(struct sysc *ddata) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci int wps, spr, error; 200362306a36Sopenharmony_ci u32 val; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci wps = 0x34; 200662306a36Sopenharmony_ci spr = 0x48; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci sysc_write(ddata, spr, 0xaaaa); 200962306a36Sopenharmony_ci error = readl_poll_timeout(ddata->module_va + wps, val, 201062306a36Sopenharmony_ci !(val & 0x10), 100, 201162306a36Sopenharmony_ci MAX_MODULE_SOFTRESET_WAIT); 201262306a36Sopenharmony_ci if (error) 201362306a36Sopenharmony_ci dev_warn(ddata->dev, "wdt disable step1 failed\n"); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci sysc_write(ddata, spr, 0x5555); 201662306a36Sopenharmony_ci error = readl_poll_timeout(ddata->module_va + wps, val, 201762306a36Sopenharmony_ci !(val & 0x10), 100, 201862306a36Sopenharmony_ci MAX_MODULE_SOFTRESET_WAIT); 201962306a36Sopenharmony_ci if (error) 202062306a36Sopenharmony_ci dev_warn(ddata->dev, "wdt disable step2 failed\n"); 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci/* PRUSS needs to set MSTANDBY_INIT inorder to idle properly */ 202462306a36Sopenharmony_cistatic void sysc_module_disable_quirk_pruss(struct sysc *ddata) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci u32 reg; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); 202962306a36Sopenharmony_ci reg |= SYSC_PRUSS_STANDBY_INIT; 203062306a36Sopenharmony_ci sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); 203162306a36Sopenharmony_ci} 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_cistatic void sysc_init_module_quirks(struct sysc *ddata) 203462306a36Sopenharmony_ci{ 203562306a36Sopenharmony_ci if (ddata->legacy_mode || !ddata->name) 203662306a36Sopenharmony_ci return; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_HDQ1W) { 203962306a36Sopenharmony_ci ddata->pre_reset_quirk = sysc_pre_reset_quirk_hdq1w; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci return; 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci#ifdef CONFIG_OMAP_GPMC_DEBUG 204562306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_GPMC_DEBUG) { 204662306a36Sopenharmony_ci ddata->cfg.quirks |= SYSC_QUIRK_NO_RESET_ON_INIT; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci return; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci#endif 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_I2C) { 205362306a36Sopenharmony_ci ddata->pre_reset_quirk = sysc_pre_reset_quirk_i2c; 205462306a36Sopenharmony_ci ddata->post_reset_quirk = sysc_post_reset_quirk_i2c; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci return; 205762306a36Sopenharmony_ci } 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS) 206062306a36Sopenharmony_ci ddata->module_enable_quirk = sysc_module_enable_quirk_aess; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_DSS_RESET) 206362306a36Sopenharmony_ci ddata->pre_reset_quirk = sysc_pre_reset_quirk_dss; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_RTC_UNLOCK) { 206662306a36Sopenharmony_ci ddata->module_unlock_quirk = sysc_module_unlock_quirk_rtc; 206762306a36Sopenharmony_ci ddata->module_lock_quirk = sysc_module_lock_quirk_rtc; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_OTG) { 207362306a36Sopenharmony_ci ddata->module_enable_quirk = sysc_module_enable_quirk_otg; 207462306a36Sopenharmony_ci ddata->module_disable_quirk = sysc_module_disable_quirk_otg; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX) 207862306a36Sopenharmony_ci ddata->module_enable_quirk = sysc_module_enable_quirk_sgx; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_WDT) { 208162306a36Sopenharmony_ci ddata->reset_done_quirk = sysc_reset_done_quirk_wdt; 208262306a36Sopenharmony_ci ddata->module_disable_quirk = sysc_reset_done_quirk_wdt; 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_PRUSS) 208662306a36Sopenharmony_ci ddata->module_disable_quirk = sysc_module_disable_quirk_pruss; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistatic int sysc_clockdomain_init(struct sysc *ddata) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); 209262306a36Sopenharmony_ci struct clk *fck = NULL, *ick = NULL; 209362306a36Sopenharmony_ci int error; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (!pdata || !pdata->init_clockdomain) 209662306a36Sopenharmony_ci return 0; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci switch (ddata->nr_clocks) { 209962306a36Sopenharmony_ci case 2: 210062306a36Sopenharmony_ci ick = ddata->clocks[SYSC_ICK]; 210162306a36Sopenharmony_ci fallthrough; 210262306a36Sopenharmony_ci case 1: 210362306a36Sopenharmony_ci fck = ddata->clocks[SYSC_FCK]; 210462306a36Sopenharmony_ci break; 210562306a36Sopenharmony_ci case 0: 210662306a36Sopenharmony_ci return 0; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci error = pdata->init_clockdomain(ddata->dev, fck, ick, &ddata->cookie); 211062306a36Sopenharmony_ci if (!error || error == -ENODEV) 211162306a36Sopenharmony_ci return 0; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci return error; 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci/* 211762306a36Sopenharmony_ci * Note that pdata->init_module() typically does a reset first. After 211862306a36Sopenharmony_ci * pdata->init_module() is done, PM runtime can be used for the interconnect 211962306a36Sopenharmony_ci * target module. 212062306a36Sopenharmony_ci */ 212162306a36Sopenharmony_cistatic int sysc_legacy_init(struct sysc *ddata) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); 212462306a36Sopenharmony_ci int error; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (!pdata || !pdata->init_module) 212762306a36Sopenharmony_ci return 0; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci error = pdata->init_module(ddata->dev, ddata->mdata, &ddata->cookie); 213062306a36Sopenharmony_ci if (error == -EEXIST) 213162306a36Sopenharmony_ci error = 0; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci return error; 213462306a36Sopenharmony_ci} 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci/* 213762306a36Sopenharmony_ci * Note that the caller must ensure the interconnect target module is enabled 213862306a36Sopenharmony_ci * before calling reset. Otherwise reset will not complete. 213962306a36Sopenharmony_ci */ 214062306a36Sopenharmony_cistatic int sysc_reset(struct sysc *ddata) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci int sysc_offset, sysc_val, error; 214362306a36Sopenharmony_ci u32 sysc_mask; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci sysc_offset = ddata->offsets[SYSC_SYSCONFIG]; 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if (ddata->legacy_mode || 214862306a36Sopenharmony_ci ddata->cap->regbits->srst_shift < 0 || 214962306a36Sopenharmony_ci ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) 215062306a36Sopenharmony_ci return 0; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci sysc_mask = BIT(ddata->cap->regbits->srst_shift); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (ddata->pre_reset_quirk) 215562306a36Sopenharmony_ci ddata->pre_reset_quirk(ddata); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci if (sysc_offset >= 0) { 215862306a36Sopenharmony_ci sysc_val = sysc_read_sysconfig(ddata); 215962306a36Sopenharmony_ci sysc_val |= sysc_mask; 216062306a36Sopenharmony_ci sysc_write(ddata, sysc_offset, sysc_val); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci /* 216362306a36Sopenharmony_ci * Some devices need a delay before reading registers 216462306a36Sopenharmony_ci * after reset. Presumably a srst_udelay is not needed 216562306a36Sopenharmony_ci * for devices that use a rstctrl register reset. 216662306a36Sopenharmony_ci */ 216762306a36Sopenharmony_ci if (ddata->cfg.srst_udelay) 216862306a36Sopenharmony_ci fsleep(ddata->cfg.srst_udelay); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci /* 217162306a36Sopenharmony_ci * Flush posted write. For devices needing srst_udelay 217262306a36Sopenharmony_ci * this should trigger an interconnect error if the 217362306a36Sopenharmony_ci * srst_udelay value is needed but not configured. 217462306a36Sopenharmony_ci */ 217562306a36Sopenharmony_ci sysc_val = sysc_read_sysconfig(ddata); 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci if (ddata->post_reset_quirk) 217962306a36Sopenharmony_ci ddata->post_reset_quirk(ddata); 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci error = sysc_wait_softreset(ddata); 218262306a36Sopenharmony_ci if (error) 218362306a36Sopenharmony_ci dev_warn(ddata->dev, "OCP softreset timed out\n"); 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci if (ddata->reset_done_quirk) 218662306a36Sopenharmony_ci ddata->reset_done_quirk(ddata); 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci return error; 218962306a36Sopenharmony_ci} 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci/* 219262306a36Sopenharmony_ci * At this point the module is configured enough to read the revision but 219362306a36Sopenharmony_ci * module may not be completely configured yet to use PM runtime. Enable 219462306a36Sopenharmony_ci * all clocks directly during init to configure the quirks needed for PM 219562306a36Sopenharmony_ci * runtime based on the revision register. 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_cistatic int sysc_init_module(struct sysc *ddata) 219862306a36Sopenharmony_ci{ 219962306a36Sopenharmony_ci bool rstctrl_deasserted = false; 220062306a36Sopenharmony_ci int error = 0; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci error = sysc_clockdomain_init(ddata); 220362306a36Sopenharmony_ci if (error) 220462306a36Sopenharmony_ci return error; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci sysc_clkdm_deny_idle(ddata); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci /* 220962306a36Sopenharmony_ci * Always enable clocks. The bootloader may or may not have enabled 221062306a36Sopenharmony_ci * the related clocks. 221162306a36Sopenharmony_ci */ 221262306a36Sopenharmony_ci error = sysc_enable_opt_clocks(ddata); 221362306a36Sopenharmony_ci if (error) 221462306a36Sopenharmony_ci return error; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci error = sysc_enable_main_clocks(ddata); 221762306a36Sopenharmony_ci if (error) 221862306a36Sopenharmony_ci goto err_opt_clocks; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) { 222162306a36Sopenharmony_ci error = reset_control_deassert(ddata->rsts); 222262306a36Sopenharmony_ci if (error) 222362306a36Sopenharmony_ci goto err_main_clocks; 222462306a36Sopenharmony_ci rstctrl_deasserted = true; 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci ddata->revision = sysc_read_revision(ddata); 222862306a36Sopenharmony_ci sysc_init_revision_quirks(ddata); 222962306a36Sopenharmony_ci sysc_init_module_quirks(ddata); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (ddata->legacy_mode) { 223262306a36Sopenharmony_ci error = sysc_legacy_init(ddata); 223362306a36Sopenharmony_ci if (error) 223462306a36Sopenharmony_ci goto err_main_clocks; 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (!ddata->legacy_mode) { 223862306a36Sopenharmony_ci error = sysc_enable_module(ddata->dev); 223962306a36Sopenharmony_ci if (error) 224062306a36Sopenharmony_ci goto err_main_clocks; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci error = sysc_reset(ddata); 224462306a36Sopenharmony_ci if (error) 224562306a36Sopenharmony_ci dev_err(ddata->dev, "Reset failed with %d\n", error); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci if (error && !ddata->legacy_mode) 224862306a36Sopenharmony_ci sysc_disable_module(ddata->dev); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cierr_main_clocks: 225162306a36Sopenharmony_ci if (error) 225262306a36Sopenharmony_ci sysc_disable_main_clocks(ddata); 225362306a36Sopenharmony_cierr_opt_clocks: 225462306a36Sopenharmony_ci /* No re-enable of clockdomain autoidle to prevent module autoidle */ 225562306a36Sopenharmony_ci if (error) { 225662306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 225762306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (error && rstctrl_deasserted && 226162306a36Sopenharmony_ci !(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) 226262306a36Sopenharmony_ci reset_control_assert(ddata->rsts); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci return error; 226562306a36Sopenharmony_ci} 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_cistatic int sysc_init_sysc_mask(struct sysc *ddata) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 227062306a36Sopenharmony_ci int error; 227162306a36Sopenharmony_ci u32 val; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci error = of_property_read_u32(np, "ti,sysc-mask", &val); 227462306a36Sopenharmony_ci if (error) 227562306a36Sopenharmony_ci return 0; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci ddata->cfg.sysc_val = val & ddata->cap->sysc_mask; 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci return 0; 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_cistatic int sysc_init_idlemode(struct sysc *ddata, u8 *idlemodes, 228362306a36Sopenharmony_ci const char *name) 228462306a36Sopenharmony_ci{ 228562306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 228662306a36Sopenharmony_ci struct property *prop; 228762306a36Sopenharmony_ci const __be32 *p; 228862306a36Sopenharmony_ci u32 val; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci of_property_for_each_u32(np, name, prop, p, val) { 229162306a36Sopenharmony_ci if (val >= SYSC_NR_IDLEMODES) { 229262306a36Sopenharmony_ci dev_err(ddata->dev, "invalid idlemode: %i\n", val); 229362306a36Sopenharmony_ci return -EINVAL; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci *idlemodes |= (1 << val); 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci return 0; 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic int sysc_init_idlemodes(struct sysc *ddata) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci int error; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci error = sysc_init_idlemode(ddata, &ddata->cfg.midlemodes, 230662306a36Sopenharmony_ci "ti,sysc-midle"); 230762306a36Sopenharmony_ci if (error) 230862306a36Sopenharmony_ci return error; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci error = sysc_init_idlemode(ddata, &ddata->cfg.sidlemodes, 231162306a36Sopenharmony_ci "ti,sysc-sidle"); 231262306a36Sopenharmony_ci if (error) 231362306a36Sopenharmony_ci return error; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci return 0; 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci/* 231962306a36Sopenharmony_ci * Only some devices on omap4 and later have SYSCONFIG reset done 232062306a36Sopenharmony_ci * bit. We can detect this if there is no SYSSTATUS at all, or the 232162306a36Sopenharmony_ci * SYSTATUS bit 0 is not used. Note that some SYSSTATUS registers 232262306a36Sopenharmony_ci * have multiple bits for the child devices like OHCI and EHCI. 232362306a36Sopenharmony_ci * Depends on SYSC being parsed first. 232462306a36Sopenharmony_ci */ 232562306a36Sopenharmony_cistatic int sysc_init_syss_mask(struct sysc *ddata) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 232862306a36Sopenharmony_ci int error; 232962306a36Sopenharmony_ci u32 val; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci error = of_property_read_u32(np, "ti,syss-mask", &val); 233262306a36Sopenharmony_ci if (error) { 233362306a36Sopenharmony_ci if ((ddata->cap->type == TI_SYSC_OMAP4 || 233462306a36Sopenharmony_ci ddata->cap->type == TI_SYSC_OMAP4_TIMER) && 233562306a36Sopenharmony_ci (ddata->cfg.sysc_val & SYSC_OMAP4_SOFTRESET)) 233662306a36Sopenharmony_ci ddata->cfg.quirks |= SYSC_QUIRK_RESET_STATUS; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci return 0; 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci if (!(val & 1) && (ddata->cfg.sysc_val & SYSC_OMAP4_SOFTRESET)) 234262306a36Sopenharmony_ci ddata->cfg.quirks |= SYSC_QUIRK_RESET_STATUS; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci ddata->cfg.syss_mask = val; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci return 0; 234762306a36Sopenharmony_ci} 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci/* 235062306a36Sopenharmony_ci * Many child device drivers need to have fck and opt clocks available 235162306a36Sopenharmony_ci * to get the clock rate for device internal configuration etc. 235262306a36Sopenharmony_ci */ 235362306a36Sopenharmony_cistatic int sysc_child_add_named_clock(struct sysc *ddata, 235462306a36Sopenharmony_ci struct device *child, 235562306a36Sopenharmony_ci const char *name) 235662306a36Sopenharmony_ci{ 235762306a36Sopenharmony_ci struct clk *clk; 235862306a36Sopenharmony_ci struct clk_lookup *l; 235962306a36Sopenharmony_ci int error = 0; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci if (!name) 236262306a36Sopenharmony_ci return 0; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci clk = clk_get(child, name); 236562306a36Sopenharmony_ci if (!IS_ERR(clk)) { 236662306a36Sopenharmony_ci error = -EEXIST; 236762306a36Sopenharmony_ci goto put_clk; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci clk = clk_get(ddata->dev, name); 237162306a36Sopenharmony_ci if (IS_ERR(clk)) 237262306a36Sopenharmony_ci return -ENODEV; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci l = clkdev_create(clk, name, dev_name(child)); 237562306a36Sopenharmony_ci if (!l) 237662306a36Sopenharmony_ci error = -ENOMEM; 237762306a36Sopenharmony_ciput_clk: 237862306a36Sopenharmony_ci clk_put(clk); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci return error; 238162306a36Sopenharmony_ci} 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_cistatic int sysc_child_add_clocks(struct sysc *ddata, 238462306a36Sopenharmony_ci struct device *child) 238562306a36Sopenharmony_ci{ 238662306a36Sopenharmony_ci int i, error; 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci for (i = 0; i < ddata->nr_clocks; i++) { 238962306a36Sopenharmony_ci error = sysc_child_add_named_clock(ddata, 239062306a36Sopenharmony_ci child, 239162306a36Sopenharmony_ci ddata->clock_roles[i]); 239262306a36Sopenharmony_ci if (error && error != -EEXIST) { 239362306a36Sopenharmony_ci dev_err(ddata->dev, "could not add child clock %s: %i\n", 239462306a36Sopenharmony_ci ddata->clock_roles[i], error); 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci return error; 239762306a36Sopenharmony_ci } 239862306a36Sopenharmony_ci } 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci return 0; 240162306a36Sopenharmony_ci} 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_cistatic struct device_type sysc_device_type = { 240462306a36Sopenharmony_ci}; 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_cistatic struct sysc *sysc_child_to_parent(struct device *dev) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct device *parent = dev->parent; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci if (!parent || parent->type != &sysc_device_type) 241162306a36Sopenharmony_ci return NULL; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci return dev_get_drvdata(parent); 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic int __maybe_unused sysc_child_runtime_suspend(struct device *dev) 241762306a36Sopenharmony_ci{ 241862306a36Sopenharmony_ci struct sysc *ddata; 241962306a36Sopenharmony_ci int error; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci ddata = sysc_child_to_parent(dev); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci error = pm_generic_runtime_suspend(dev); 242462306a36Sopenharmony_ci if (error) 242562306a36Sopenharmony_ci return error; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (!ddata->enabled) 242862306a36Sopenharmony_ci return 0; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci return sysc_runtime_suspend(ddata->dev); 243162306a36Sopenharmony_ci} 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_cistatic int __maybe_unused sysc_child_runtime_resume(struct device *dev) 243462306a36Sopenharmony_ci{ 243562306a36Sopenharmony_ci struct sysc *ddata; 243662306a36Sopenharmony_ci int error; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci ddata = sysc_child_to_parent(dev); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci if (!ddata->enabled) { 244162306a36Sopenharmony_ci error = sysc_runtime_resume(ddata->dev); 244262306a36Sopenharmony_ci if (error < 0) 244362306a36Sopenharmony_ci dev_err(ddata->dev, 244462306a36Sopenharmony_ci "%s error: %i\n", __func__, error); 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci return pm_generic_runtime_resume(dev); 244862306a36Sopenharmony_ci} 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 245162306a36Sopenharmony_cistatic int sysc_child_suspend_noirq(struct device *dev) 245262306a36Sopenharmony_ci{ 245362306a36Sopenharmony_ci struct sysc *ddata; 245462306a36Sopenharmony_ci int error; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci ddata = sysc_child_to_parent(dev); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci dev_dbg(ddata->dev, "%s %s\n", __func__, 245962306a36Sopenharmony_ci ddata->name ? ddata->name : ""); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci error = pm_generic_suspend_noirq(dev); 246262306a36Sopenharmony_ci if (error) { 246362306a36Sopenharmony_ci dev_err(dev, "%s error at %i: %i\n", 246462306a36Sopenharmony_ci __func__, __LINE__, error); 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci return error; 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci if (!pm_runtime_status_suspended(dev)) { 247062306a36Sopenharmony_ci error = pm_generic_runtime_suspend(dev); 247162306a36Sopenharmony_ci if (error) { 247262306a36Sopenharmony_ci dev_dbg(dev, "%s busy at %i: %i\n", 247362306a36Sopenharmony_ci __func__, __LINE__, error); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci return 0; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci error = sysc_runtime_suspend(ddata->dev); 247962306a36Sopenharmony_ci if (error) { 248062306a36Sopenharmony_ci dev_err(dev, "%s error at %i: %i\n", 248162306a36Sopenharmony_ci __func__, __LINE__, error); 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci return error; 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci ddata->child_needs_resume = true; 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci return 0; 249062306a36Sopenharmony_ci} 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_cistatic int sysc_child_resume_noirq(struct device *dev) 249362306a36Sopenharmony_ci{ 249462306a36Sopenharmony_ci struct sysc *ddata; 249562306a36Sopenharmony_ci int error; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci ddata = sysc_child_to_parent(dev); 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci dev_dbg(ddata->dev, "%s %s\n", __func__, 250062306a36Sopenharmony_ci ddata->name ? ddata->name : ""); 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci if (ddata->child_needs_resume) { 250362306a36Sopenharmony_ci ddata->child_needs_resume = false; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci error = sysc_runtime_resume(ddata->dev); 250662306a36Sopenharmony_ci if (error) 250762306a36Sopenharmony_ci dev_err(ddata->dev, 250862306a36Sopenharmony_ci "%s runtime resume error: %i\n", 250962306a36Sopenharmony_ci __func__, error); 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci error = pm_generic_runtime_resume(dev); 251262306a36Sopenharmony_ci if (error) 251362306a36Sopenharmony_ci dev_err(ddata->dev, 251462306a36Sopenharmony_ci "%s generic runtime resume: %i\n", 251562306a36Sopenharmony_ci __func__, error); 251662306a36Sopenharmony_ci } 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci return pm_generic_resume_noirq(dev); 251962306a36Sopenharmony_ci} 252062306a36Sopenharmony_ci#endif 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cistatic struct dev_pm_domain sysc_child_pm_domain = { 252362306a36Sopenharmony_ci .ops = { 252462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend, 252562306a36Sopenharmony_ci sysc_child_runtime_resume, 252662306a36Sopenharmony_ci NULL) 252762306a36Sopenharmony_ci USE_PLATFORM_PM_SLEEP_OPS 252862306a36Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq, 252962306a36Sopenharmony_ci sysc_child_resume_noirq) 253062306a36Sopenharmony_ci } 253162306a36Sopenharmony_ci}; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci/* Caller needs to take list_lock if ever used outside of cpu_pm */ 253462306a36Sopenharmony_cistatic void sysc_reinit_modules(struct sysc_soc_info *soc) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci struct sysc_module *module; 253762306a36Sopenharmony_ci struct sysc *ddata; 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci list_for_each_entry(module, &sysc_soc->restored_modules, node) { 254062306a36Sopenharmony_ci ddata = module->ddata; 254162306a36Sopenharmony_ci sysc_reinit_module(ddata, ddata->enabled); 254262306a36Sopenharmony_ci } 254362306a36Sopenharmony_ci} 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci/** 254662306a36Sopenharmony_ci * sysc_context_notifier - optionally reset and restore module after idle 254762306a36Sopenharmony_ci * @nb: notifier block 254862306a36Sopenharmony_ci * @cmd: unused 254962306a36Sopenharmony_ci * @v: unused 255062306a36Sopenharmony_ci * 255162306a36Sopenharmony_ci * Some interconnect target modules need to be restored, or reset and restored 255262306a36Sopenharmony_ci * on CPU_PM CPU_PM_CLUSTER_EXIT notifier. This is needed at least for am335x 255362306a36Sopenharmony_ci * OTG and GPMC target modules even if the modules are unused. 255462306a36Sopenharmony_ci */ 255562306a36Sopenharmony_cistatic int sysc_context_notifier(struct notifier_block *nb, unsigned long cmd, 255662306a36Sopenharmony_ci void *v) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci struct sysc_soc_info *soc; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci soc = container_of(nb, struct sysc_soc_info, nb); 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci switch (cmd) { 256362306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 256462306a36Sopenharmony_ci break; 256562306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */ 256662306a36Sopenharmony_ci break; 256762306a36Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 256862306a36Sopenharmony_ci sysc_reinit_modules(soc); 256962306a36Sopenharmony_ci break; 257062306a36Sopenharmony_ci } 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci return NOTIFY_OK; 257362306a36Sopenharmony_ci} 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci/** 257662306a36Sopenharmony_ci * sysc_add_restored - optionally add reset and restore quirk hanlling 257762306a36Sopenharmony_ci * @ddata: device data 257862306a36Sopenharmony_ci */ 257962306a36Sopenharmony_cistatic void sysc_add_restored(struct sysc *ddata) 258062306a36Sopenharmony_ci{ 258162306a36Sopenharmony_ci struct sysc_module *restored_module; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci restored_module = kzalloc(sizeof(*restored_module), GFP_KERNEL); 258462306a36Sopenharmony_ci if (!restored_module) 258562306a36Sopenharmony_ci return; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci restored_module->ddata = ddata; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci mutex_lock(&sysc_soc->list_lock); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci list_add(&restored_module->node, &sysc_soc->restored_modules); 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci if (sysc_soc->nb.notifier_call) 259462306a36Sopenharmony_ci goto out_unlock; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci sysc_soc->nb.notifier_call = sysc_context_notifier; 259762306a36Sopenharmony_ci cpu_pm_register_notifier(&sysc_soc->nb); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ciout_unlock: 260062306a36Sopenharmony_ci mutex_unlock(&sysc_soc->list_lock); 260162306a36Sopenharmony_ci} 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci/** 260462306a36Sopenharmony_ci * sysc_legacy_idle_quirk - handle children in omap_device compatible way 260562306a36Sopenharmony_ci * @ddata: device driver data 260662306a36Sopenharmony_ci * @child: child device driver 260762306a36Sopenharmony_ci * 260862306a36Sopenharmony_ci * Allow idle for child devices as done with _od_runtime_suspend(). 260962306a36Sopenharmony_ci * Otherwise many child devices will not idle because of the permanent 261062306a36Sopenharmony_ci * parent usecount set in pm_runtime_irq_safe(). 261162306a36Sopenharmony_ci * 261262306a36Sopenharmony_ci * Note that the long term solution is to just modify the child device 261362306a36Sopenharmony_ci * drivers to not set pm_runtime_irq_safe() and then this can be just 261462306a36Sopenharmony_ci * dropped. 261562306a36Sopenharmony_ci */ 261662306a36Sopenharmony_cistatic void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child) 261762306a36Sopenharmony_ci{ 261862306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) 261962306a36Sopenharmony_ci dev_pm_domain_set(child, &sysc_child_pm_domain); 262062306a36Sopenharmony_ci} 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_cistatic int sysc_notifier_call(struct notifier_block *nb, 262362306a36Sopenharmony_ci unsigned long event, void *device) 262462306a36Sopenharmony_ci{ 262562306a36Sopenharmony_ci struct device *dev = device; 262662306a36Sopenharmony_ci struct sysc *ddata; 262762306a36Sopenharmony_ci int error; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci ddata = sysc_child_to_parent(dev); 263062306a36Sopenharmony_ci if (!ddata) 263162306a36Sopenharmony_ci return NOTIFY_DONE; 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci switch (event) { 263462306a36Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 263562306a36Sopenharmony_ci error = sysc_child_add_clocks(ddata, dev); 263662306a36Sopenharmony_ci if (error) 263762306a36Sopenharmony_ci return error; 263862306a36Sopenharmony_ci sysc_legacy_idle_quirk(ddata, dev); 263962306a36Sopenharmony_ci break; 264062306a36Sopenharmony_ci default: 264162306a36Sopenharmony_ci break; 264262306a36Sopenharmony_ci } 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci return NOTIFY_DONE; 264562306a36Sopenharmony_ci} 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_cistatic struct notifier_block sysc_nb = { 264862306a36Sopenharmony_ci .notifier_call = sysc_notifier_call, 264962306a36Sopenharmony_ci}; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci/* Device tree configured quirks */ 265262306a36Sopenharmony_cistruct sysc_dts_quirk { 265362306a36Sopenharmony_ci const char *name; 265462306a36Sopenharmony_ci u32 mask; 265562306a36Sopenharmony_ci}; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_cistatic const struct sysc_dts_quirk sysc_dts_quirks[] = { 265862306a36Sopenharmony_ci { .name = "ti,no-idle-on-init", 265962306a36Sopenharmony_ci .mask = SYSC_QUIRK_NO_IDLE_ON_INIT, }, 266062306a36Sopenharmony_ci { .name = "ti,no-reset-on-init", 266162306a36Sopenharmony_ci .mask = SYSC_QUIRK_NO_RESET_ON_INIT, }, 266262306a36Sopenharmony_ci { .name = "ti,no-idle", 266362306a36Sopenharmony_ci .mask = SYSC_QUIRK_NO_IDLE, }, 266462306a36Sopenharmony_ci}; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_cistatic void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, 266762306a36Sopenharmony_ci bool is_child) 266862306a36Sopenharmony_ci{ 266962306a36Sopenharmony_ci const struct property *prop; 267062306a36Sopenharmony_ci int i, len; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) { 267362306a36Sopenharmony_ci const char *name = sysc_dts_quirks[i].name; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci prop = of_get_property(np, name, &len); 267662306a36Sopenharmony_ci if (!prop) 267762306a36Sopenharmony_ci continue; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci ddata->cfg.quirks |= sysc_dts_quirks[i].mask; 268062306a36Sopenharmony_ci if (is_child) { 268162306a36Sopenharmony_ci dev_warn(ddata->dev, 268262306a36Sopenharmony_ci "dts flag should be at module level for %s\n", 268362306a36Sopenharmony_ci name); 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci } 268662306a36Sopenharmony_ci} 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_cistatic int sysc_init_dts_quirks(struct sysc *ddata) 268962306a36Sopenharmony_ci{ 269062306a36Sopenharmony_ci struct device_node *np = ddata->dev->of_node; 269162306a36Sopenharmony_ci int error; 269262306a36Sopenharmony_ci u32 val; 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL); 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci sysc_parse_dts_quirks(ddata, np, false); 269762306a36Sopenharmony_ci error = of_property_read_u32(np, "ti,sysc-delay-us", &val); 269862306a36Sopenharmony_ci if (!error) { 269962306a36Sopenharmony_ci if (val > 255) { 270062306a36Sopenharmony_ci dev_warn(ddata->dev, "bad ti,sysc-delay-us: %i\n", 270162306a36Sopenharmony_ci val); 270262306a36Sopenharmony_ci } 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci ddata->cfg.srst_udelay = (u8)val; 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci return 0; 270862306a36Sopenharmony_ci} 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_cistatic void sysc_unprepare(struct sysc *ddata) 271162306a36Sopenharmony_ci{ 271262306a36Sopenharmony_ci int i; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci if (!ddata->clocks) 271562306a36Sopenharmony_ci return; 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci for (i = 0; i < SYSC_MAX_CLOCKS; i++) { 271862306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(ddata->clocks[i])) 271962306a36Sopenharmony_ci clk_unprepare(ddata->clocks[i]); 272062306a36Sopenharmony_ci } 272162306a36Sopenharmony_ci} 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci/* 272462306a36Sopenharmony_ci * Common sysc register bits found on omap2, also known as type1 272562306a36Sopenharmony_ci */ 272662306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap2 = { 272762306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 272862306a36Sopenharmony_ci .midle_shift = 12, 272962306a36Sopenharmony_ci .sidle_shift = 3, 273062306a36Sopenharmony_ci .clkact_shift = 8, 273162306a36Sopenharmony_ci .emufree_shift = 5, 273262306a36Sopenharmony_ci .enwkup_shift = 2, 273362306a36Sopenharmony_ci .srst_shift = 1, 273462306a36Sopenharmony_ci .autoidle_shift = 0, 273562306a36Sopenharmony_ci}; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap2 = { 273862306a36Sopenharmony_ci .type = TI_SYSC_OMAP2, 273962306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY | SYSC_OMAP2_EMUFREE | 274062306a36Sopenharmony_ci SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_SOFTRESET | 274162306a36Sopenharmony_ci SYSC_OMAP2_AUTOIDLE, 274262306a36Sopenharmony_ci .regbits = &sysc_regbits_omap2, 274362306a36Sopenharmony_ci}; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci/* All omap2 and 3 timers, and timers 1, 2 & 10 on omap 4 and 5 */ 274662306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap2_timer = { 274762306a36Sopenharmony_ci .type = TI_SYSC_OMAP2_TIMER, 274862306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY | SYSC_OMAP2_EMUFREE | 274962306a36Sopenharmony_ci SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_SOFTRESET | 275062306a36Sopenharmony_ci SYSC_OMAP2_AUTOIDLE, 275162306a36Sopenharmony_ci .regbits = &sysc_regbits_omap2, 275262306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_USE_CLOCKACT, 275362306a36Sopenharmony_ci}; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci/* 275662306a36Sopenharmony_ci * SHAM2 (SHA1/MD5) sysc found on omap3, a variant of sysc_regbits_omap2 275762306a36Sopenharmony_ci * with different sidle position 275862306a36Sopenharmony_ci */ 275962306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap3_sham = { 276062306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 276162306a36Sopenharmony_ci .midle_shift = -ENODEV, 276262306a36Sopenharmony_ci .sidle_shift = 4, 276362306a36Sopenharmony_ci .clkact_shift = -ENODEV, 276462306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 276562306a36Sopenharmony_ci .srst_shift = 1, 276662306a36Sopenharmony_ci .autoidle_shift = 0, 276762306a36Sopenharmony_ci .emufree_shift = -ENODEV, 276862306a36Sopenharmony_ci}; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap3_sham = { 277162306a36Sopenharmony_ci .type = TI_SYSC_OMAP3_SHAM, 277262306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_SOFTRESET | SYSC_OMAP2_AUTOIDLE, 277362306a36Sopenharmony_ci .regbits = &sysc_regbits_omap3_sham, 277462306a36Sopenharmony_ci}; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci/* 277762306a36Sopenharmony_ci * AES register bits found on omap3 and later, a variant of 277862306a36Sopenharmony_ci * sysc_regbits_omap2 with different sidle position 277962306a36Sopenharmony_ci */ 278062306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap3_aes = { 278162306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 278262306a36Sopenharmony_ci .midle_shift = -ENODEV, 278362306a36Sopenharmony_ci .sidle_shift = 6, 278462306a36Sopenharmony_ci .clkact_shift = -ENODEV, 278562306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 278662306a36Sopenharmony_ci .srst_shift = 1, 278762306a36Sopenharmony_ci .autoidle_shift = 0, 278862306a36Sopenharmony_ci .emufree_shift = -ENODEV, 278962306a36Sopenharmony_ci}; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap3_aes = { 279262306a36Sopenharmony_ci .type = TI_SYSC_OMAP3_AES, 279362306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_SOFTRESET | SYSC_OMAP2_AUTOIDLE, 279462306a36Sopenharmony_ci .regbits = &sysc_regbits_omap3_aes, 279562306a36Sopenharmony_ci}; 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci/* 279862306a36Sopenharmony_ci * Common sysc register bits found on omap4, also known as type2 279962306a36Sopenharmony_ci */ 280062306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap4 = { 280162306a36Sopenharmony_ci .dmadisable_shift = 16, 280262306a36Sopenharmony_ci .midle_shift = 4, 280362306a36Sopenharmony_ci .sidle_shift = 2, 280462306a36Sopenharmony_ci .clkact_shift = -ENODEV, 280562306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 280662306a36Sopenharmony_ci .emufree_shift = 1, 280762306a36Sopenharmony_ci .srst_shift = 0, 280862306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 280962306a36Sopenharmony_ci}; 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4 = { 281262306a36Sopenharmony_ci .type = TI_SYSC_OMAP4, 281362306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP4_DMADISABLE | SYSC_OMAP4_FREEEMU | 281462306a36Sopenharmony_ci SYSC_OMAP4_SOFTRESET, 281562306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4, 281662306a36Sopenharmony_ci}; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4_timer = { 281962306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_TIMER, 282062306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP4_DMADISABLE | SYSC_OMAP4_FREEEMU | 282162306a36Sopenharmony_ci SYSC_OMAP4_SOFTRESET, 282262306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4, 282362306a36Sopenharmony_ci}; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci/* 282662306a36Sopenharmony_ci * Common sysc register bits found on omap4, also known as type3 282762306a36Sopenharmony_ci */ 282862306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap4_simple = { 282962306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 283062306a36Sopenharmony_ci .midle_shift = 2, 283162306a36Sopenharmony_ci .sidle_shift = 0, 283262306a36Sopenharmony_ci .clkact_shift = -ENODEV, 283362306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 283462306a36Sopenharmony_ci .srst_shift = -ENODEV, 283562306a36Sopenharmony_ci .emufree_shift = -ENODEV, 283662306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 283762306a36Sopenharmony_ci}; 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4_simple = { 284062306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_SIMPLE, 284162306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4_simple, 284262306a36Sopenharmony_ci}; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci/* 284562306a36Sopenharmony_ci * SmartReflex sysc found on omap34xx 284662306a36Sopenharmony_ci */ 284762306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap34xx_sr = { 284862306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 284962306a36Sopenharmony_ci .midle_shift = -ENODEV, 285062306a36Sopenharmony_ci .sidle_shift = -ENODEV, 285162306a36Sopenharmony_ci .clkact_shift = 20, 285262306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 285362306a36Sopenharmony_ci .srst_shift = -ENODEV, 285462306a36Sopenharmony_ci .emufree_shift = -ENODEV, 285562306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 285662306a36Sopenharmony_ci}; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_34xx_sr = { 285962306a36Sopenharmony_ci .type = TI_SYSC_OMAP34XX_SR, 286062306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY, 286162306a36Sopenharmony_ci .regbits = &sysc_regbits_omap34xx_sr, 286262306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED | 286362306a36Sopenharmony_ci SYSC_QUIRK_LEGACY_IDLE, 286462306a36Sopenharmony_ci}; 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci/* 286762306a36Sopenharmony_ci * SmartReflex sysc found on omap36xx and later 286862306a36Sopenharmony_ci */ 286962306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap36xx_sr = { 287062306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 287162306a36Sopenharmony_ci .midle_shift = -ENODEV, 287262306a36Sopenharmony_ci .sidle_shift = 24, 287362306a36Sopenharmony_ci .clkact_shift = -ENODEV, 287462306a36Sopenharmony_ci .enwkup_shift = 26, 287562306a36Sopenharmony_ci .srst_shift = -ENODEV, 287662306a36Sopenharmony_ci .emufree_shift = -ENODEV, 287762306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 287862306a36Sopenharmony_ci}; 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_36xx_sr = { 288162306a36Sopenharmony_ci .type = TI_SYSC_OMAP36XX_SR, 288262306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP, 288362306a36Sopenharmony_ci .regbits = &sysc_regbits_omap36xx_sr, 288462306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE, 288562306a36Sopenharmony_ci}; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4_sr = { 288862306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_SR, 288962306a36Sopenharmony_ci .regbits = &sysc_regbits_omap36xx_sr, 289062306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_LEGACY_IDLE, 289162306a36Sopenharmony_ci}; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci/* 289462306a36Sopenharmony_ci * McASP register bits found on omap4 and later 289562306a36Sopenharmony_ci */ 289662306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap4_mcasp = { 289762306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 289862306a36Sopenharmony_ci .midle_shift = -ENODEV, 289962306a36Sopenharmony_ci .sidle_shift = 0, 290062306a36Sopenharmony_ci .clkact_shift = -ENODEV, 290162306a36Sopenharmony_ci .enwkup_shift = -ENODEV, 290262306a36Sopenharmony_ci .srst_shift = -ENODEV, 290362306a36Sopenharmony_ci .emufree_shift = -ENODEV, 290462306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 290562306a36Sopenharmony_ci}; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4_mcasp = { 290862306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_MCASP, 290962306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4_mcasp, 291062306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, 291162306a36Sopenharmony_ci}; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci/* 291462306a36Sopenharmony_ci * McASP found on dra7 and later 291562306a36Sopenharmony_ci */ 291662306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_dra7_mcasp = { 291762306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_SIMPLE, 291862306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4_simple, 291962306a36Sopenharmony_ci .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, 292062306a36Sopenharmony_ci}; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci/* 292362306a36Sopenharmony_ci * FS USB host found on omap4 and later 292462306a36Sopenharmony_ci */ 292562306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_omap4_usb_host_fs = { 292662306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 292762306a36Sopenharmony_ci .midle_shift = -ENODEV, 292862306a36Sopenharmony_ci .sidle_shift = 24, 292962306a36Sopenharmony_ci .clkact_shift = -ENODEV, 293062306a36Sopenharmony_ci .enwkup_shift = 26, 293162306a36Sopenharmony_ci .srst_shift = -ENODEV, 293262306a36Sopenharmony_ci .emufree_shift = -ENODEV, 293362306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 293462306a36Sopenharmony_ci}; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_omap4_usb_host_fs = { 293762306a36Sopenharmony_ci .type = TI_SYSC_OMAP4_USB_HOST_FS, 293862306a36Sopenharmony_ci .sysc_mask = SYSC_OMAP2_ENAWAKEUP, 293962306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4_usb_host_fs, 294062306a36Sopenharmony_ci}; 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_cistatic const struct sysc_regbits sysc_regbits_dra7_mcan = { 294362306a36Sopenharmony_ci .dmadisable_shift = -ENODEV, 294462306a36Sopenharmony_ci .midle_shift = -ENODEV, 294562306a36Sopenharmony_ci .sidle_shift = -ENODEV, 294662306a36Sopenharmony_ci .clkact_shift = -ENODEV, 294762306a36Sopenharmony_ci .enwkup_shift = 4, 294862306a36Sopenharmony_ci .srst_shift = 0, 294962306a36Sopenharmony_ci .emufree_shift = -ENODEV, 295062306a36Sopenharmony_ci .autoidle_shift = -ENODEV, 295162306a36Sopenharmony_ci}; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_dra7_mcan = { 295462306a36Sopenharmony_ci .type = TI_SYSC_DRA7_MCAN, 295562306a36Sopenharmony_ci .sysc_mask = SYSC_DRA7_MCAN_ENAWAKEUP | SYSC_OMAP4_SOFTRESET, 295662306a36Sopenharmony_ci .regbits = &sysc_regbits_dra7_mcan, 295762306a36Sopenharmony_ci .mod_quirks = SYSS_QUIRK_RESETDONE_INVERTED, 295862306a36Sopenharmony_ci}; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci/* 296162306a36Sopenharmony_ci * PRUSS found on some AM33xx, AM437x and AM57xx SoCs 296262306a36Sopenharmony_ci */ 296362306a36Sopenharmony_cistatic const struct sysc_capabilities sysc_pruss = { 296462306a36Sopenharmony_ci .type = TI_SYSC_PRUSS, 296562306a36Sopenharmony_ci .sysc_mask = SYSC_PRUSS_STANDBY_INIT | SYSC_PRUSS_SUB_MWAIT, 296662306a36Sopenharmony_ci .regbits = &sysc_regbits_omap4_simple, 296762306a36Sopenharmony_ci .mod_quirks = SYSC_MODULE_QUIRK_PRUSS, 296862306a36Sopenharmony_ci}; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_cistatic int sysc_init_pdata(struct sysc *ddata) 297162306a36Sopenharmony_ci{ 297262306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); 297362306a36Sopenharmony_ci struct ti_sysc_module_data *mdata; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci if (!pdata) 297662306a36Sopenharmony_ci return 0; 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci mdata = devm_kzalloc(ddata->dev, sizeof(*mdata), GFP_KERNEL); 297962306a36Sopenharmony_ci if (!mdata) 298062306a36Sopenharmony_ci return -ENOMEM; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci if (ddata->legacy_mode) { 298362306a36Sopenharmony_ci mdata->name = ddata->legacy_mode; 298462306a36Sopenharmony_ci mdata->module_pa = ddata->module_pa; 298562306a36Sopenharmony_ci mdata->module_size = ddata->module_size; 298662306a36Sopenharmony_ci mdata->offsets = ddata->offsets; 298762306a36Sopenharmony_ci mdata->nr_offsets = SYSC_MAX_REGS; 298862306a36Sopenharmony_ci mdata->cap = ddata->cap; 298962306a36Sopenharmony_ci mdata->cfg = &ddata->cfg; 299062306a36Sopenharmony_ci } 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci ddata->mdata = mdata; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci return 0; 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_cistatic int sysc_init_match(struct sysc *ddata) 299862306a36Sopenharmony_ci{ 299962306a36Sopenharmony_ci const struct sysc_capabilities *cap; 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci cap = of_device_get_match_data(ddata->dev); 300262306a36Sopenharmony_ci if (!cap) 300362306a36Sopenharmony_ci return -EINVAL; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci ddata->cap = cap; 300662306a36Sopenharmony_ci if (ddata->cap) 300762306a36Sopenharmony_ci ddata->cfg.quirks |= ddata->cap->mod_quirks; 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci return 0; 301062306a36Sopenharmony_ci} 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_cistatic void ti_sysc_idle(struct work_struct *work) 301362306a36Sopenharmony_ci{ 301462306a36Sopenharmony_ci struct sysc *ddata; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci ddata = container_of(work, struct sysc, idle_work.work); 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci /* 301962306a36Sopenharmony_ci * One time decrement of clock usage counts if left on from init. 302062306a36Sopenharmony_ci * Note that we disable opt clocks unconditionally in this case 302162306a36Sopenharmony_ci * as they are enabled unconditionally during init without 302262306a36Sopenharmony_ci * considering sysc_opt_clks_needed() at that point. 302362306a36Sopenharmony_ci */ 302462306a36Sopenharmony_ci if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE | 302562306a36Sopenharmony_ci SYSC_QUIRK_NO_IDLE_ON_INIT)) { 302662306a36Sopenharmony_ci sysc_disable_main_clocks(ddata); 302762306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 302862306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci /* Keep permanent PM runtime usage count for SYSC_QUIRK_NO_IDLE */ 303262306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE) 303362306a36Sopenharmony_ci return; 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci /* 303662306a36Sopenharmony_ci * Decrement PM runtime usage count for SYSC_QUIRK_NO_IDLE_ON_INIT 303762306a36Sopenharmony_ci * and SYSC_QUIRK_NO_RESET_ON_INIT 303862306a36Sopenharmony_ci */ 303962306a36Sopenharmony_ci if (pm_runtime_active(ddata->dev)) 304062306a36Sopenharmony_ci pm_runtime_put_sync(ddata->dev); 304162306a36Sopenharmony_ci} 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci/* 304462306a36Sopenharmony_ci * SoC model and features detection. Only needed for SoCs that need 304562306a36Sopenharmony_ci * special handling for quirks, no need to list others. 304662306a36Sopenharmony_ci */ 304762306a36Sopenharmony_cistatic const struct soc_device_attribute sysc_soc_match[] = { 304862306a36Sopenharmony_ci SOC_FLAG("OMAP242*", SOC_2420), 304962306a36Sopenharmony_ci SOC_FLAG("OMAP243*", SOC_2430), 305062306a36Sopenharmony_ci SOC_FLAG("AM35*", SOC_AM35), 305162306a36Sopenharmony_ci SOC_FLAG("OMAP3[45]*", SOC_3430), 305262306a36Sopenharmony_ci SOC_FLAG("OMAP3[67]*", SOC_3630), 305362306a36Sopenharmony_ci SOC_FLAG("OMAP443*", SOC_4430), 305462306a36Sopenharmony_ci SOC_FLAG("OMAP446*", SOC_4460), 305562306a36Sopenharmony_ci SOC_FLAG("OMAP447*", SOC_4470), 305662306a36Sopenharmony_ci SOC_FLAG("OMAP54*", SOC_5430), 305762306a36Sopenharmony_ci SOC_FLAG("AM433", SOC_AM3), 305862306a36Sopenharmony_ci SOC_FLAG("AM43*", SOC_AM4), 305962306a36Sopenharmony_ci SOC_FLAG("DRA7*", SOC_DRA7), 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci { /* sentinel */ } 306262306a36Sopenharmony_ci}; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci/* 306562306a36Sopenharmony_ci * List of SoCs variants with disabled features. By default we assume all 306662306a36Sopenharmony_ci * devices in the device tree are available so no need to list those SoCs. 306762306a36Sopenharmony_ci */ 306862306a36Sopenharmony_cistatic const struct soc_device_attribute sysc_soc_feat_match[] = { 306962306a36Sopenharmony_ci /* OMAP3430/3530 and AM3517 variants with some accelerators disabled */ 307062306a36Sopenharmony_ci SOC_FLAG("AM3505", DIS_SGX), 307162306a36Sopenharmony_ci SOC_FLAG("OMAP3525", DIS_SGX), 307262306a36Sopenharmony_ci SOC_FLAG("OMAP3515", DIS_IVA | DIS_SGX), 307362306a36Sopenharmony_ci SOC_FLAG("OMAP3503", DIS_ISP | DIS_IVA | DIS_SGX), 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci /* OMAP3630/DM3730 variants with some accelerators disabled */ 307662306a36Sopenharmony_ci SOC_FLAG("AM3703", DIS_IVA | DIS_SGX), 307762306a36Sopenharmony_ci SOC_FLAG("DM3725", DIS_SGX), 307862306a36Sopenharmony_ci SOC_FLAG("OMAP3611", DIS_ISP | DIS_IVA | DIS_SGX), 307962306a36Sopenharmony_ci SOC_FLAG("OMAP3615/AM3715", DIS_IVA), 308062306a36Sopenharmony_ci SOC_FLAG("OMAP3621", DIS_ISP), 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci { /* sentinel */ } 308362306a36Sopenharmony_ci}; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_cistatic int sysc_add_disabled(unsigned long base) 308662306a36Sopenharmony_ci{ 308762306a36Sopenharmony_ci struct sysc_address *disabled_module; 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci disabled_module = kzalloc(sizeof(*disabled_module), GFP_KERNEL); 309062306a36Sopenharmony_ci if (!disabled_module) 309162306a36Sopenharmony_ci return -ENOMEM; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci disabled_module->base = base; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci mutex_lock(&sysc_soc->list_lock); 309662306a36Sopenharmony_ci list_add(&disabled_module->node, &sysc_soc->disabled_modules); 309762306a36Sopenharmony_ci mutex_unlock(&sysc_soc->list_lock); 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci return 0; 310062306a36Sopenharmony_ci} 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci/* 310362306a36Sopenharmony_ci * One time init to detect the booted SoC, disable unavailable features 310462306a36Sopenharmony_ci * and initialize list for optional cpu_pm notifier. 310562306a36Sopenharmony_ci * 310662306a36Sopenharmony_ci * Note that we initialize static data shared across all ti-sysc instances 310762306a36Sopenharmony_ci * so ddata is only used for SoC type. This can be called from module_init 310862306a36Sopenharmony_ci * once we no longer need to rely on platform data. 310962306a36Sopenharmony_ci */ 311062306a36Sopenharmony_cistatic int sysc_init_static_data(struct sysc *ddata) 311162306a36Sopenharmony_ci{ 311262306a36Sopenharmony_ci const struct soc_device_attribute *match; 311362306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata; 311462306a36Sopenharmony_ci unsigned long features = 0; 311562306a36Sopenharmony_ci struct device_node *np; 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci if (sysc_soc) 311862306a36Sopenharmony_ci return 0; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci sysc_soc = kzalloc(sizeof(*sysc_soc), GFP_KERNEL); 312162306a36Sopenharmony_ci if (!sysc_soc) 312262306a36Sopenharmony_ci return -ENOMEM; 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci mutex_init(&sysc_soc->list_lock); 312562306a36Sopenharmony_ci INIT_LIST_HEAD(&sysc_soc->disabled_modules); 312662306a36Sopenharmony_ci INIT_LIST_HEAD(&sysc_soc->restored_modules); 312762306a36Sopenharmony_ci sysc_soc->general_purpose = true; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci pdata = dev_get_platdata(ddata->dev); 313062306a36Sopenharmony_ci if (pdata && pdata->soc_type_gp) 313162306a36Sopenharmony_ci sysc_soc->general_purpose = pdata->soc_type_gp(); 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci match = soc_device_match(sysc_soc_match); 313462306a36Sopenharmony_ci if (match && match->data) 313562306a36Sopenharmony_ci sysc_soc->soc = (enum sysc_soc)(uintptr_t)match->data; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci /* 313862306a36Sopenharmony_ci * Check and warn about possible old incomplete dtb. We now want to see 313962306a36Sopenharmony_ci * simple-pm-bus instead of simple-bus in the dtb for genpd using SoCs. 314062306a36Sopenharmony_ci */ 314162306a36Sopenharmony_ci switch (sysc_soc->soc) { 314262306a36Sopenharmony_ci case SOC_AM3: 314362306a36Sopenharmony_ci case SOC_AM4: 314462306a36Sopenharmony_ci case SOC_4430 ... SOC_4470: 314562306a36Sopenharmony_ci case SOC_5430: 314662306a36Sopenharmony_ci case SOC_DRA7: 314762306a36Sopenharmony_ci np = of_find_node_by_path("/ocp"); 314862306a36Sopenharmony_ci WARN_ONCE(np && of_device_is_compatible(np, "simple-bus"), 314962306a36Sopenharmony_ci "ti-sysc: Incomplete old dtb, please update\n"); 315062306a36Sopenharmony_ci break; 315162306a36Sopenharmony_ci default: 315262306a36Sopenharmony_ci break; 315362306a36Sopenharmony_ci } 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci /* Ignore devices that are not available on HS and EMU SoCs */ 315662306a36Sopenharmony_ci if (!sysc_soc->general_purpose) { 315762306a36Sopenharmony_ci switch (sysc_soc->soc) { 315862306a36Sopenharmony_ci case SOC_3430 ... SOC_3630: 315962306a36Sopenharmony_ci sysc_add_disabled(0x48304000); /* timer12 */ 316062306a36Sopenharmony_ci break; 316162306a36Sopenharmony_ci case SOC_AM3: 316262306a36Sopenharmony_ci sysc_add_disabled(0x48310000); /* rng */ 316362306a36Sopenharmony_ci break; 316462306a36Sopenharmony_ci default: 316562306a36Sopenharmony_ci break; 316662306a36Sopenharmony_ci } 316762306a36Sopenharmony_ci } 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci match = soc_device_match(sysc_soc_feat_match); 317062306a36Sopenharmony_ci if (!match) 317162306a36Sopenharmony_ci return 0; 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ci if (match->data) 317462306a36Sopenharmony_ci features = (unsigned long)match->data; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci /* 317762306a36Sopenharmony_ci * Add disabled devices to the list based on the module base. 317862306a36Sopenharmony_ci * Note that this must be done before we attempt to access the 317962306a36Sopenharmony_ci * device and have module revision checks working. 318062306a36Sopenharmony_ci */ 318162306a36Sopenharmony_ci if (features & DIS_ISP) 318262306a36Sopenharmony_ci sysc_add_disabled(0x480bd400); 318362306a36Sopenharmony_ci if (features & DIS_IVA) 318462306a36Sopenharmony_ci sysc_add_disabled(0x5d000000); 318562306a36Sopenharmony_ci if (features & DIS_SGX) 318662306a36Sopenharmony_ci sysc_add_disabled(0x50000000); 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_ci return 0; 318962306a36Sopenharmony_ci} 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_cistatic void sysc_cleanup_static_data(void) 319262306a36Sopenharmony_ci{ 319362306a36Sopenharmony_ci struct sysc_module *restored_module; 319462306a36Sopenharmony_ci struct sysc_address *disabled_module; 319562306a36Sopenharmony_ci struct list_head *pos, *tmp; 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci if (!sysc_soc) 319862306a36Sopenharmony_ci return; 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci if (sysc_soc->nb.notifier_call) 320162306a36Sopenharmony_ci cpu_pm_unregister_notifier(&sysc_soc->nb); 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci mutex_lock(&sysc_soc->list_lock); 320462306a36Sopenharmony_ci list_for_each_safe(pos, tmp, &sysc_soc->restored_modules) { 320562306a36Sopenharmony_ci restored_module = list_entry(pos, struct sysc_module, node); 320662306a36Sopenharmony_ci list_del(pos); 320762306a36Sopenharmony_ci kfree(restored_module); 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) { 321062306a36Sopenharmony_ci disabled_module = list_entry(pos, struct sysc_address, node); 321162306a36Sopenharmony_ci list_del(pos); 321262306a36Sopenharmony_ci kfree(disabled_module); 321362306a36Sopenharmony_ci } 321462306a36Sopenharmony_ci mutex_unlock(&sysc_soc->list_lock); 321562306a36Sopenharmony_ci} 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_cistatic int sysc_check_disabled_devices(struct sysc *ddata) 321862306a36Sopenharmony_ci{ 321962306a36Sopenharmony_ci struct sysc_address *disabled_module; 322062306a36Sopenharmony_ci int error = 0; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci mutex_lock(&sysc_soc->list_lock); 322362306a36Sopenharmony_ci list_for_each_entry(disabled_module, &sysc_soc->disabled_modules, node) { 322462306a36Sopenharmony_ci if (ddata->module_pa == disabled_module->base) { 322562306a36Sopenharmony_ci dev_dbg(ddata->dev, "module disabled for this SoC\n"); 322662306a36Sopenharmony_ci error = -ENODEV; 322762306a36Sopenharmony_ci break; 322862306a36Sopenharmony_ci } 322962306a36Sopenharmony_ci } 323062306a36Sopenharmony_ci mutex_unlock(&sysc_soc->list_lock); 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci return error; 323362306a36Sopenharmony_ci} 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci/* 323662306a36Sopenharmony_ci * Ignore timers tagged with no-reset and no-idle. These are likely in use, 323762306a36Sopenharmony_ci * for example by drivers/clocksource/timer-ti-dm-systimer.c. If more checks 323862306a36Sopenharmony_ci * are needed, we could also look at the timer register configuration. 323962306a36Sopenharmony_ci */ 324062306a36Sopenharmony_cistatic int sysc_check_active_timer(struct sysc *ddata) 324162306a36Sopenharmony_ci{ 324262306a36Sopenharmony_ci int error; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci if (ddata->cap->type != TI_SYSC_OMAP2_TIMER && 324562306a36Sopenharmony_ci ddata->cap->type != TI_SYSC_OMAP4_TIMER) 324662306a36Sopenharmony_ci return 0; 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci /* 324962306a36Sopenharmony_ci * Quirk for omap3 beagleboard revision A to B4 to use gpt12. 325062306a36Sopenharmony_ci * Revision C and later are fixed with commit 23885389dbbb ("ARM: 325162306a36Sopenharmony_ci * dts: Fix timer regression for beagleboard revision c"). This all 325262306a36Sopenharmony_ci * can be dropped if we stop supporting old beagleboard revisions 325362306a36Sopenharmony_ci * A to B4 at some point. 325462306a36Sopenharmony_ci */ 325562306a36Sopenharmony_ci if (sysc_soc->soc == SOC_3430 || sysc_soc->soc == SOC_AM35) 325662306a36Sopenharmony_ci error = -ENXIO; 325762306a36Sopenharmony_ci else 325862306a36Sopenharmony_ci error = -EBUSY; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci if ((ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) && 326162306a36Sopenharmony_ci (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)) 326262306a36Sopenharmony_ci return error; 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci return 0; 326562306a36Sopenharmony_ci} 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_cistatic const struct of_device_id sysc_match_table[] = { 326862306a36Sopenharmony_ci { .compatible = "simple-bus", }, 326962306a36Sopenharmony_ci { /* sentinel */ }, 327062306a36Sopenharmony_ci}; 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_cistatic int sysc_probe(struct platform_device *pdev) 327362306a36Sopenharmony_ci{ 327462306a36Sopenharmony_ci struct ti_sysc_platform_data *pdata = dev_get_platdata(&pdev->dev); 327562306a36Sopenharmony_ci struct sysc *ddata; 327662306a36Sopenharmony_ci int error; 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 327962306a36Sopenharmony_ci if (!ddata) 328062306a36Sopenharmony_ci return -ENOMEM; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci ddata->offsets[SYSC_REVISION] = -ENODEV; 328362306a36Sopenharmony_ci ddata->offsets[SYSC_SYSCONFIG] = -ENODEV; 328462306a36Sopenharmony_ci ddata->offsets[SYSC_SYSSTATUS] = -ENODEV; 328562306a36Sopenharmony_ci ddata->dev = &pdev->dev; 328662306a36Sopenharmony_ci platform_set_drvdata(pdev, ddata); 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci error = sysc_init_static_data(ddata); 328962306a36Sopenharmony_ci if (error) 329062306a36Sopenharmony_ci return error; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci error = sysc_init_match(ddata); 329362306a36Sopenharmony_ci if (error) 329462306a36Sopenharmony_ci return error; 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci error = sysc_init_dts_quirks(ddata); 329762306a36Sopenharmony_ci if (error) 329862306a36Sopenharmony_ci return error; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci error = sysc_map_and_check_registers(ddata); 330162306a36Sopenharmony_ci if (error) 330262306a36Sopenharmony_ci return error; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci error = sysc_init_sysc_mask(ddata); 330562306a36Sopenharmony_ci if (error) 330662306a36Sopenharmony_ci return error; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci error = sysc_init_idlemodes(ddata); 330962306a36Sopenharmony_ci if (error) 331062306a36Sopenharmony_ci return error; 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci error = sysc_init_syss_mask(ddata); 331362306a36Sopenharmony_ci if (error) 331462306a36Sopenharmony_ci return error; 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci error = sysc_init_pdata(ddata); 331762306a36Sopenharmony_ci if (error) 331862306a36Sopenharmony_ci return error; 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci sysc_init_early_quirks(ddata); 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci error = sysc_check_disabled_devices(ddata); 332362306a36Sopenharmony_ci if (error) 332462306a36Sopenharmony_ci return error; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci error = sysc_check_active_timer(ddata); 332762306a36Sopenharmony_ci if (error == -ENXIO) 332862306a36Sopenharmony_ci ddata->reserved = true; 332962306a36Sopenharmony_ci else if (error) 333062306a36Sopenharmony_ci return error; 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci error = sysc_get_clocks(ddata); 333362306a36Sopenharmony_ci if (error) 333462306a36Sopenharmony_ci return error; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci error = sysc_init_resets(ddata); 333762306a36Sopenharmony_ci if (error) 333862306a36Sopenharmony_ci goto unprepare; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci error = sysc_init_module(ddata); 334162306a36Sopenharmony_ci if (error) 334262306a36Sopenharmony_ci goto unprepare; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci pm_runtime_enable(ddata->dev); 334562306a36Sopenharmony_ci error = pm_runtime_resume_and_get(ddata->dev); 334662306a36Sopenharmony_ci if (error < 0) { 334762306a36Sopenharmony_ci pm_runtime_disable(ddata->dev); 334862306a36Sopenharmony_ci goto unprepare; 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* Balance use counts as PM runtime should have enabled these all */ 335262306a36Sopenharmony_ci if (!(ddata->cfg.quirks & 335362306a36Sopenharmony_ci (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))) { 335462306a36Sopenharmony_ci sysc_disable_main_clocks(ddata); 335562306a36Sopenharmony_ci sysc_disable_opt_clocks(ddata); 335662306a36Sopenharmony_ci sysc_clkdm_allow_idle(ddata); 335762306a36Sopenharmony_ci } 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) 336062306a36Sopenharmony_ci reset_control_assert(ddata->rsts); 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci sysc_show_registers(ddata); 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_ci ddata->dev->type = &sysc_device_type; 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci if (!ddata->reserved) { 336762306a36Sopenharmony_ci error = of_platform_populate(ddata->dev->of_node, 336862306a36Sopenharmony_ci sysc_match_table, 336962306a36Sopenharmony_ci pdata ? pdata->auxdata : NULL, 337062306a36Sopenharmony_ci ddata->dev); 337162306a36Sopenharmony_ci if (error) 337262306a36Sopenharmony_ci goto err; 337362306a36Sopenharmony_ci } 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci INIT_DELAYED_WORK(&ddata->idle_work, ti_sysc_idle); 337662306a36Sopenharmony_ci 337762306a36Sopenharmony_ci /* At least earlycon won't survive without deferred idle */ 337862306a36Sopenharmony_ci if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE | 337962306a36Sopenharmony_ci SYSC_QUIRK_NO_IDLE_ON_INIT | 338062306a36Sopenharmony_ci SYSC_QUIRK_NO_RESET_ON_INIT)) { 338162306a36Sopenharmony_ci schedule_delayed_work(&ddata->idle_work, 3000); 338262306a36Sopenharmony_ci } else { 338362306a36Sopenharmony_ci pm_runtime_put(&pdev->dev); 338462306a36Sopenharmony_ci } 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_CTX_LOST) 338762306a36Sopenharmony_ci sysc_add_restored(ddata); 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci return 0; 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_cierr: 339262306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 339362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 339462306a36Sopenharmony_ciunprepare: 339562306a36Sopenharmony_ci sysc_unprepare(ddata); 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci return error; 339862306a36Sopenharmony_ci} 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_cistatic int sysc_remove(struct platform_device *pdev) 340162306a36Sopenharmony_ci{ 340262306a36Sopenharmony_ci struct sysc *ddata = platform_get_drvdata(pdev); 340362306a36Sopenharmony_ci int error; 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci /* Device can still be enabled, see deferred idle quirk in probe */ 340662306a36Sopenharmony_ci if (cancel_delayed_work_sync(&ddata->idle_work)) 340762306a36Sopenharmony_ci ti_sysc_idle(&ddata->idle_work.work); 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci error = pm_runtime_resume_and_get(ddata->dev); 341062306a36Sopenharmony_ci if (error < 0) { 341162306a36Sopenharmony_ci pm_runtime_disable(ddata->dev); 341262306a36Sopenharmony_ci goto unprepare; 341362306a36Sopenharmony_ci } 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci of_platform_depopulate(&pdev->dev); 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 341862306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci if (!reset_control_status(ddata->rsts)) 342162306a36Sopenharmony_ci reset_control_assert(ddata->rsts); 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ciunprepare: 342462306a36Sopenharmony_ci sysc_unprepare(ddata); 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci return 0; 342762306a36Sopenharmony_ci} 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_cistatic const struct of_device_id sysc_match[] = { 343062306a36Sopenharmony_ci { .compatible = "ti,sysc-omap2", .data = &sysc_omap2, }, 343162306a36Sopenharmony_ci { .compatible = "ti,sysc-omap2-timer", .data = &sysc_omap2_timer, }, 343262306a36Sopenharmony_ci { .compatible = "ti,sysc-omap4", .data = &sysc_omap4, }, 343362306a36Sopenharmony_ci { .compatible = "ti,sysc-omap4-timer", .data = &sysc_omap4_timer, }, 343462306a36Sopenharmony_ci { .compatible = "ti,sysc-omap4-simple", .data = &sysc_omap4_simple, }, 343562306a36Sopenharmony_ci { .compatible = "ti,sysc-omap3430-sr", .data = &sysc_34xx_sr, }, 343662306a36Sopenharmony_ci { .compatible = "ti,sysc-omap3630-sr", .data = &sysc_36xx_sr, }, 343762306a36Sopenharmony_ci { .compatible = "ti,sysc-omap4-sr", .data = &sysc_omap4_sr, }, 343862306a36Sopenharmony_ci { .compatible = "ti,sysc-omap3-sham", .data = &sysc_omap3_sham, }, 343962306a36Sopenharmony_ci { .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, }, 344062306a36Sopenharmony_ci { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, }, 344162306a36Sopenharmony_ci { .compatible = "ti,sysc-dra7-mcasp", .data = &sysc_dra7_mcasp, }, 344262306a36Sopenharmony_ci { .compatible = "ti,sysc-usb-host-fs", 344362306a36Sopenharmony_ci .data = &sysc_omap4_usb_host_fs, }, 344462306a36Sopenharmony_ci { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, 344562306a36Sopenharmony_ci { .compatible = "ti,sysc-pruss", .data = &sysc_pruss, }, 344662306a36Sopenharmony_ci { }, 344762306a36Sopenharmony_ci}; 344862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sysc_match); 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_cistatic struct platform_driver sysc_driver = { 345162306a36Sopenharmony_ci .probe = sysc_probe, 345262306a36Sopenharmony_ci .remove = sysc_remove, 345362306a36Sopenharmony_ci .driver = { 345462306a36Sopenharmony_ci .name = "ti-sysc", 345562306a36Sopenharmony_ci .of_match_table = sysc_match, 345662306a36Sopenharmony_ci .pm = &sysc_pm_ops, 345762306a36Sopenharmony_ci }, 345862306a36Sopenharmony_ci}; 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_cistatic int __init sysc_init(void) 346162306a36Sopenharmony_ci{ 346262306a36Sopenharmony_ci bus_register_notifier(&platform_bus_type, &sysc_nb); 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_ci return platform_driver_register(&sysc_driver); 346562306a36Sopenharmony_ci} 346662306a36Sopenharmony_cimodule_init(sysc_init); 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_cistatic void __exit sysc_exit(void) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci bus_unregister_notifier(&platform_bus_type, &sysc_nb); 347162306a36Sopenharmony_ci platform_driver_unregister(&sysc_driver); 347262306a36Sopenharmony_ci sysc_cleanup_static_data(); 347362306a36Sopenharmony_ci} 347462306a36Sopenharmony_cimodule_exit(sysc_exit); 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ciMODULE_DESCRIPTION("TI sysc interconnect target driver"); 347762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3478