/* * Copyright (c) 2025-2026 Texas Instruments Incorporated - https://www.ti.com * * SPDX-License-Identifier: BSD-3-Clause */ /* * Power/Sleep Controller (PSC) Driver * * This driver manages the PSC hardware including power domain state transitions, * LPSC module clock gating, and dependency management between modules and power domains. */ #include #include #include #include #include #include #include #include #include #include #define PSC_PTCMD 0x120U #define PSC_PTCMD_H 0x124U #define PSC_PTSTAT 0x128U #define PSC_PTSTAT_H 0x12CU #define PSC_PDSTAT_BASE 0x200U #define PSC_PDCTL_BASE 0x300U #define PSC_MDSTAT_BASE 0x800U #define PSC_MDCTL_BASE 0xa00U static inline uint32_t psc_reg(uint32_t base, uint32_t id) { return base + (4U * id); } #define MDSTAT_STATE_MASK 0x3fU #define MDSTAT_BUSY_MASK 0x30U #define MDSTAT_STATE_SWRSTDISABLE 0x00U #define MDSTAT_STATE_SYNCRST 0x01U #define MDSTAT_STATE_DISABLE 0x02U #define MDSTAT_STATE_ENABLE 0x03U #define MDSTAT_MRSTDONE BIT(11) /* Module reset done */ #define MDSTAT_LRSTDONE BIT(9) /* Local reset done */ #define MDCTL_RESET_ISO BIT(12) /* Enable reset isolation */ #define MDCTL_LRST BIT(8) /* Assert local reset when 0 */ #define PDSTAT_STATE_MASK 0x1fU #define PDCTL_STATE_MASK BIT(0) #define PDCTL_STATE_OFF 0U #define PDCTL_STATE_ON 1U /* Timeout values for PSC state transitions */ #define PSC_TRANSITION_TIMEOUT_US 10000U /* 10ms timeout */ #define PSC_TRANSITION_DELAY_US 100U /* 100us delay between checks */ #define PSC_TRANSITION_RETRY_COUNT (PSC_TRANSITION_TIMEOUT_US / PSC_TRANSITION_DELAY_US) /* Module state return values */ #define TI_LPSC_STATE_DISABLED 0U /* Module is disabled */ #define TI_LPSC_STATE_ENABLED 1U /* Module is enabled or in retention */ #define TI_LPSC_STATE_TRANSITIONING 2U /* Module is transitioning */ static struct ti_device *psc_devs; static void lpsc_module_get_internal(struct ti_device *dev, struct ti_lpsc_module *module, bool use, bool ret); static void lpsc_module_put_internal(struct ti_device *dev, struct ti_lpsc_module *module, bool use, bool ret); static uint32_t psc_read(struct ti_device *dev, uint32_t reg) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); if (psc->base == 0U) return 0U; return mmio_read_32(psc->base + reg); } static void psc_write(struct ti_device *dev, uint32_t val, uint32_t reg) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); if (psc->base == 0U) return; mmio_write_32(psc->base + reg, val); } ti_pd_idx_t ti_psc_pd_idx(struct ti_device *dev, const struct ti_psc_pd *pd) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ptrdiff_t diff = pd - psc->powerdomains; return (ti_pd_idx_t) diff; } static inline struct ti_psc_pd *psc_idx2pd(const struct ti_psc_drv_data *psc, ti_pd_idx_t id) { return &psc->powerdomains[id]; } static const struct ti_psc_pd_data *get_psc_pd_data(struct ti_device *dev, struct ti_psc_pd *pd) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); return &psc->pd_data[ti_psc_pd_idx(dev, pd)]; } ti_lpsc_idx_t ti_lpsc_module_idx(struct ti_device *dev, const struct ti_lpsc_module *module) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ptrdiff_t diff = module - psc->modules; return (ti_lpsc_idx_t) diff; } static inline struct ti_lpsc_module *psc_idx2mod(const struct ti_psc_drv_data *psc, ti_lpsc_idx_t id) { return &psc->modules[id]; } void ti_psc_pd_wait(struct ti_device *dev, struct ti_psc_pd *pd) { uint32_t psc_ptstat = PSC_PTSTAT; ti_pd_idx_t psc_idx = ti_psc_pd_idx(dev, pd); if (0U == (get_psc_pd_data(dev, pd)->flags & TI_PSC_PD_ALWAYSON)) { uint32_t retry_count = PSC_TRANSITION_RETRY_COUNT; /* power domain >= 32 uses PSC_PTSTAT_H register */ if (psc_idx >= 32U) { psc_ptstat = PSC_PTSTAT_H; } while ((((psc_read(dev, psc_ptstat)) & BIT(psc_idx % 32U)) != 0U) && (retry_count > 0U)) { udelay(PSC_TRANSITION_DELAY_US); retry_count--; } if (retry_count == 0U) { /* Directly convert to psc to get psc_idx */ VERBOSE("PSC transition timeout: psc_id=%d pd_id=%d\n", (ti_to_psc_drv_data(ti_get_drv_data(dev)))->psc_idx, ti_psc_pd_idx(dev, pd)); } } } static void pd_initiate(struct ti_device *dev, struct ti_psc_pd *pd) { uint32_t psc_ptcmd = PSC_PTCMD; ti_pd_idx_t psc_idx = ti_psc_pd_idx(dev, pd); /* power domain >= 32 uses PSC_PTCMD_H register */ if (psc_idx >= 32U) { psc_ptcmd = PSC_PTCMD_H; } /* Note: This is a state machine reg */ psc_write(dev, (uint32_t) BIT(psc_idx % 32U), psc_ptcmd); } /** * psc_pd_clk_get() - Enable clocks necessary for power domain * @data: The const data for the power domain * * This function calls ti_clk_get on each of the clocks listed * under clock_dep for a given power domain. Some power domains * require certain clocks to be running while the power domain * is in transition or on. */ static void psc_pd_clk_get(const struct ti_psc_pd_data *data) { uint32_t i; for (i = 0U; i < ARRAY_SIZE(data->clock_dep); i++) { struct ti_clk *clkp = ti_clk_lookup(data->clock_dep[i]); if (clkp != NULL) { (void) ti_clk_get(clkp); } } } void ti_psc_pd_get(struct ti_device *dev, struct ti_psc_pd *pd) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_psc_pd_idx(dev, pd); uint32_t pdctl; VERBOSE("PD_GET: psc_id=%d pd_id=%d use_count=%d\n", psc->psc_idx, idx, pd->use_count); if ((pd->use_count++) != 0U) { return; } if ((psc->pd_data[idx].flags & TI_PSC_PD_ALWAYSON) != 0U) { return; } /* Verify any previous transitions have completed */ ti_psc_pd_wait(dev, pd); if ((psc->pd_data[idx].flags & TI_PSC_PD_DEPENDS) != 0U) { ti_psc_pd_get(dev, psc_idx2pd(psc, (ti_pd_idx_t) psc->pd_data[idx].depends)); } psc_pd_clk_get(&psc->pd_data[idx]); pdctl = psc_read(dev, psc_reg(PSC_PDCTL_BASE, idx)); if ((pdctl & PDCTL_STATE_MASK) != PDCTL_STATE_ON) { /* Avoid redundant power-up transitions */ pdctl &= ~PDCTL_STATE_MASK; pdctl |= PDCTL_STATE_ON; /* Note: This is a state machine reg */ psc_write(dev, pdctl, psc_reg(PSC_PDCTL_BASE, idx)); pd_initiate(dev, pd); ti_psc_pd_wait(dev, pd); } psc->data->pds_enabled |= (uint32_t) BIT(idx); } /** * psc_pd_clk_put() - Disable clocks necessary for power domain * @data: The const data for the power domain * * This function calls ti_clk_put on each of the clocks listed * under clock_dep for a given power domain. Some power domains * require certain clocks to be running while the power domain * is in transition or on. */ static void psc_pd_clk_put(const struct ti_psc_pd_data *data) { uint32_t i; for (i = 0U; i < ARRAY_SIZE(data->clock_dep); i++) { struct ti_clk *clkp = ti_clk_lookup(data->clock_dep[i]); if (clkp != NULL) { ti_clk_put(clkp); } } } void ti_psc_pd_put(struct ti_device *dev, struct ti_psc_pd *pd) { uint32_t pdctl; const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_psc_pd_idx(dev, pd); VERBOSE("PD_PUT: psc_id=%d pd_id=%d use_count=%d\n", psc->psc_idx, idx, pd->use_count); if ((--pd->use_count) != 0U) { return; } if ((psc->pd_data[idx].flags & TI_PSC_PD_ALWAYSON) != 0U) { return; } /* Verify any previous transitions have completed */ ti_psc_pd_wait(dev, pd); pdctl = psc_read(dev, psc_reg(PSC_PDCTL_BASE, idx)); if ((pdctl & PDCTL_STATE_MASK) != PDCTL_STATE_OFF) { /* Avoid redundant power-up transitions */ pdctl &= ~PDCTL_STATE_MASK; pdctl |= PDCTL_STATE_OFF; /* Note: This is a state machine reg */ psc_write(dev, pdctl, psc_reg(PSC_PDCTL_BASE, idx)); pd_initiate(dev, pd); ti_psc_pd_wait(dev, pd); } psc_pd_clk_put(&psc->pd_data[idx]); if ((psc->pd_data[idx].flags & TI_PSC_PD_DEPENDS) != 0U) { ti_psc_pd_put(dev, psc_idx2pd(psc, (ti_pd_idx_t) psc->pd_data[idx].depends)); } psc->data->pds_enabled &= ~((uint32_t) BIT(idx)); } /** * lpsc_module_sync_state() - Sync the hardware state of a module with it's software state. * @dev: The device struct for this PSC. * @module: The module data for this module. * @domain_reset: True if syncing just prior to a domain reset, * in which case actual transitions are avoided. * * This function examines the current state of a given LPSC module and * compares it with the last programmed state. If there have been changes it * programs the hardware appropriately. It also enables/disables any * dependencies as necessary. */ static void lpsc_module_sync_state(struct ti_device *dev, struct ti_lpsc_module *module, bool domain_reset) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t mdctl; uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; struct ti_psc_pd *pd = psc_idx2pd(psc, (ti_pd_idx_t) data->powerdomain); uint8_t state; /* Target module state */ uint8_t old_state; /* Original module state */ bool new_mrst_ret; /* MRST induced retention */ bool old_msrt_ret; bool old_pwr; /* Power domain should be enabled */ bool old_ret; /* Retention of any kind */ bool old_en; /* Enabled (clocks running) */ bool old_rst; bool new_pwr; bool new_ret; bool new_en; bool new_rst; bool transition; /* A state transition is necessary */ bool get_en; /* Moving from disabled to enabled */ bool get_ret; /* Moving from off to retention */ bool get_pwr; /* Moving from power domain off to on */ bool put_en; /* Moving from enabled to disabled */ bool put_ret; /* Moving from retention to off */ bool put_pwr; /* Moving from power domain on to off */ /* * Determine target state based on usage counts and module reset: * * +---------+-----------+-------+-------------------------------+ * + Enabled | Retention | Reset | State | * +=========+===========+=======+===============================+ * | No | No | NA | SwRstDisabled (may power off) | * +---------+-----------+-------+-------------------------------+ * | No | Yes | No | Disabled | * +---------+-----------+-------+-------------------------------+ * | No | Yes | Yes | SwRstDisabled (powered-on) | * +---------+-----------+-------+-------------------------------+ * | Yes | NA | No | Enabled | * +---------+-----------+-------+-------------------------------+ * | Yes | NA | Yes | SyncReset | * +---------+-----------+-------+-------------------------------+ */ new_mrst_ret = false; if ((module->use_count == 0U) && (module->ret_count == 0U)) { state = MDSTAT_STATE_SWRSTDISABLE; } else if (module->use_count == 0U) { /* Retention enabled, but module disabled */ if (module->mrst_active == 0U) { state = MDSTAT_STATE_DISABLE; } else { new_mrst_ret = true; state = MDSTAT_STATE_SWRSTDISABLE; } } else { /* Module enabled (retention setting is don't care) */ if (module->mrst_active == 0U) { state = MDSTAT_STATE_ENABLE; } else { state = MDSTAT_STATE_SYNCRST; } } /* Promote target state based on disallowed states */ if ((state == MDSTAT_STATE_SWRSTDISABLE) && ((data->flags & TI_LPSC_NO_MODULE_RESET) != 0U)) { state = MDSTAT_STATE_DISABLE; new_mrst_ret = false; } if ((state == MDSTAT_STATE_DISABLE) && ((data->flags & TI_LPSC_NO_CLOCK_GATING) != 0U)) { state = MDSTAT_STATE_ENABLE; } if ((state == MDSTAT_STATE_SYNCRST) && ((data->flags & TI_LPSC_NO_MODULE_RESET) != 0U)) { state = MDSTAT_STATE_ENABLE; } /* Track transition of old state to new state */ old_state = module->sw_state; module->sw_state = state; old_msrt_ret = module->sw_mrst_ret; module->sw_mrst_ret = new_mrst_ret; /* Previous setting of retention, enable, and reset */ old_ret = old_state != MDSTAT_STATE_SWRSTDISABLE; old_pwr = old_ret || old_msrt_ret; old_en = (old_state == MDSTAT_STATE_SYNCRST) || (old_state == MDSTAT_STATE_ENABLE); old_rst = (old_state != MDSTAT_STATE_ENABLE) && (old_state != MDSTAT_STATE_DISABLE); /* New setting of retention, enable, and reset */ new_ret = state != MDSTAT_STATE_SWRSTDISABLE; new_pwr = new_ret || new_mrst_ret; new_en = (state == MDSTAT_STATE_SYNCRST) || (state == MDSTAT_STATE_ENABLE); new_rst = (state != MDSTAT_STATE_ENABLE) && (state != MDSTAT_STATE_DISABLE); /* Are we transitioning from no retention/enable to retention/enable? */ get_ret = !old_ret && new_ret; get_pwr = !old_pwr && new_pwr; get_en = !old_en && new_en; /* Are we transitioning from retention/enable to no retention/enable? */ put_ret = old_ret && !new_ret; put_pwr = old_pwr && !new_pwr; put_en = old_en && !new_en; /* Make sure our parent LPSC is enabled as necessary */ if ((get_en || get_ret) && ((data->flags & TI_LPSC_DEPENDS) != 0UL)) { const struct ti_psc_drv_data *depends_psc = psc; struct ti_device *depends_dev = dev; if (data->depends_psc_idx != psc->psc_idx) { depends_dev = ti_psc_lookup((ti_psc_idx_t) data->depends_psc_idx); depends_psc = ti_to_psc_drv_data(ti_get_drv_data(depends_dev)); } if (depends_dev == NULL) { VERBOSE("ACTION FAIL: PSC_INVALID_DEP_DATA dep_pd_id=%d pd_id=%d pos=1\n", psc->psc_idx, data->depends_psc_idx); } else { /* * Moving from a clock stop state to a clock enabled * state or from a retention disable to a retention * enabled state, bump the reference count on our * dependency. */ lpsc_module_get_internal(depends_dev, psc_idx2mod(depends_psc, data->depends), get_en, get_ret); } } if (old_rst && !new_rst) { /* Coming out of reset, bump the context loss count. */ module->loss_count++; } if (get_pwr) { /* Make sure pd is on. */ ti_psc_pd_get(dev, pd); } mdctl = psc_read(dev, psc_reg(PSC_MDCTL_BASE, idx)); transition = (mdctl & MDSTAT_STATE_MASK) != state; if (transition && (domain_reset == false)) { /* Verify any previous transitions have completed */ ti_psc_pd_wait(dev, pd); mdctl &= ~MDSTAT_STATE_MASK; mdctl |= (uint32_t) state; /* Note: This is a state machine reg */ psc_write(dev, mdctl, psc_reg(PSC_MDCTL_BASE, idx)); } if (domain_reset == false) { if (put_pwr) { /* Module is ready for power down, drop ref count on pd */ ti_psc_pd_put(dev, pd); if ((pd->use_count != 0U) && transition) { /* * If ti_psc_pd_put has a use count of zero, it already * initaited the transition, otherwise we need to * do the transition ourselves. */ pd_initiate(dev, pd); } } else if (transition) { /* * Initiate transition */ pd_initiate(dev, pd); } } /* Allow our parent LPSC to be disabled as necessary */ if ((put_en || put_ret) && ((data->flags & TI_LPSC_DEPENDS) != 0UL)) { const struct ti_psc_drv_data *depends_psc = psc; struct ti_device *depends_dev = dev; if (data->depends_psc_idx != psc->psc_idx) { depends_dev = ti_psc_lookup((ti_psc_idx_t) data->depends_psc_idx); depends_psc = ti_to_psc_drv_data(ti_get_drv_data(depends_dev)); } if (depends_dev == NULL) { VERBOSE("ACTION FAIL: PSC_INVALID_DEP_DATA dep_pd_id=%d pd_id=%d pos=2\n", psc->psc_idx, data->depends_psc_idx); } else if ((domain_reset == true) && (depends_dev == dev)) { /* Ignore self dependencies during domain reset */ } else { /* * Moving from a clock enabled state to a clock stop * state or from a retention enable to a retention * disabled state, drop the reference count on our * dependency. */ lpsc_module_put_internal(depends_dev, psc_idx2mod(depends_psc, data->depends), put_en, put_ret); } } } uint32_t ti_lpsc_module_get_state(struct ti_device *dev, struct ti_lpsc_module *module) { uint32_t idx = ti_lpsc_module_idx(dev, module); uint8_t state; uint32_t ret; state = (uint8_t)(psc_read(dev, psc_reg(PSC_MDSTAT_BASE, idx)) & MDSTAT_STATE_MASK); if (state == MDSTAT_STATE_SWRSTDISABLE) { ret = TI_LPSC_STATE_DISABLED; } else if ((state == MDSTAT_STATE_DISABLE) || (state == MDSTAT_STATE_ENABLE) || (state == MDSTAT_STATE_SYNCRST)) { ret = TI_LPSC_STATE_ENABLED; } else { ret = TI_LPSC_STATE_TRANSITIONING; } return ret; } void ti_lpsc_module_set_reset_iso(struct ti_device *dev, struct ti_lpsc_module *module, bool enable) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; bool is_enabled; uint32_t mdctl; if (0U == (data->flags & TI_LPSC_HAS_RESET_ISO)) { return; } mdctl = psc_read(dev, psc_reg(PSC_MDCTL_BASE, idx)); is_enabled = (mdctl & MDCTL_RESET_ISO) != 0U; if (enable != is_enabled) { if (enable) { mdctl |= MDCTL_RESET_ISO; } else { mdctl &= ~MDCTL_RESET_ISO; } /* Note: This is a state machine reg */ psc_write(dev, mdctl, psc_reg(PSC_MDCTL_BASE, idx)); } } bool ti_lpsc_module_get_reset_iso(struct ti_device *dev, struct ti_lpsc_module *module) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; bool ret; if (0U == (data->flags & TI_LPSC_HAS_RESET_ISO)) { ret = false; } else { ret = (psc_read(dev, psc_reg(PSC_MDCTL_BASE, idx)) & MDCTL_RESET_ISO) != 0U; } return ret; } /* Does not bump context loss count */ void ti_lpsc_module_set_local_reset(struct ti_device *dev, struct ti_lpsc_module *module, bool enable) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; bool is_enabled; uint32_t mdctl; if (0U == (data->flags & TI_LPSC_HAS_LOCAL_RESET)) { return; } mdctl = psc_read(dev, (uint32_t) psc_reg(PSC_MDCTL_BASE, idx)); is_enabled = (mdctl & MDCTL_LRST) == 0U; if (enable != is_enabled) { VERBOSE("SET_LOCAL_RESET: psc_id=%d lpsc_id=%d enable=%d\n", psc->psc_idx, idx, ((enable == true) ? 1U : 0U)); if (enable) { mdctl &= ~MDCTL_LRST; } else { mdctl |= MDCTL_LRST; } /* Note: This is a state machine reg */ psc_write(dev, mdctl, psc_reg(PSC_MDCTL_BASE, idx)); } } void ti_lpsc_module_set_module_reset(struct ti_device *dev, struct ti_lpsc_module *module, bool enable) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; bool is_enabled; is_enabled = module->mrst_active != 0U; if ((enable != is_enabled) && ((data->flags & TI_LPSC_NO_MODULE_RESET) == 0U)) { VERBOSE("SET_MODULE_RESET/SET_LOCAL_RESET: psc_id=%d lpsc_id=%d enable=%d\n", psc->psc_idx, idx, (enable ? 1U : 0U)); if (enable) { module->mrst_active = 1U; } else { module->mrst_active = 0U; } lpsc_module_sync_state(dev, module, false); ti_lpsc_module_wait(dev, module); } } bool ti_lpsc_module_get_local_reset(struct ti_device *dev, struct ti_lpsc_module *module) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; bool ret; if (0U == (data->flags & TI_LPSC_HAS_LOCAL_RESET)) { ret = false; } else { ret = (psc_read(dev, (uint32_t) psc_reg(PSC_MDCTL_BASE, idx)) & MDCTL_LRST) == 0U; } return ret; } bool ti_lpsc_module_get_module_reset(struct ti_device *dev __unused, const struct ti_lpsc_module *module) { return module->mrst_active == 1U; } void ti_lpsc_module_wait(struct ti_device *dev, struct ti_lpsc_module *module) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, module); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; struct ti_psc_pd *pd = psc_idx2pd(psc, (ti_pd_idx_t) data->powerdomain); uint32_t retry_count = PSC_TRANSITION_RETRY_COUNT; ti_psc_pd_wait(dev, pd); while ((((psc_read(dev, psc_reg(PSC_MDSTAT_BASE, idx))) & MDSTAT_BUSY_MASK) != 0U) && (retry_count > 0U)) { udelay(PSC_TRANSITION_DELAY_US); retry_count--; } if (retry_count == 0U) { VERBOSE("PSC module transition timeout: psc_id=%d lpsc_id=%d\n", psc->psc_idx, idx); } /* Only wait for reset to complete if module is in use */ if ((module->use_count) != 0U) { uint32_t mask = MDSTAT_MRSTDONE; retry_count = PSC_TRANSITION_RETRY_COUNT; if (!ti_lpsc_module_get_local_reset(dev, module)) { mask |= MDSTAT_LRSTDONE; } while ((0U == ((psc_read(dev, psc_reg(PSC_MDSTAT_BASE, idx))) & mask)) && (retry_count > 0U)) { udelay(PSC_TRANSITION_DELAY_US); retry_count--; } if (retry_count == 0U) { VERBOSE("PSC module reset done timeout: psc_id=%d lpsc_id=%d\n", psc->psc_idx, idx); } } } static void lpsc_module_clk_get(struct ti_device *dev, struct ti_lpsc_module *mod) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, mod); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; /* Get clock dependency - currently only one clock supported */ if (data->clock_dep[0] != 0U) { struct ti_clk *clkp = ti_clk_lookup(data->clock_dep[0]); if (clkp != NULL) { (void) ti_clk_get(clkp); } } } /** * lpsc_module_clk_put() - Disable clocks necessary for LPSC module * @dev: The device for this PSC. * @mod: The const data for the LPSC module. * @wait: True to wait for the module to complete transitioning * before disabling clocks. Set to false when called as * part of a domain reset. * * This function calls ti_clk_put on each of the clocks listed * under clock_dep for a given LPSC module. Some modules * require certain clocks to be running while the module * is in transition or on. */ static void lpsc_module_clk_put(struct ti_device *dev, struct ti_lpsc_module *mod, bool wait) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx = ti_lpsc_module_idx(dev, mod); const struct ti_lpsc_module_data *data = &psc->mod_data[idx]; /* Put clock dependency - currently only one clock supported */ if (data->clock_dep[0] != 0U) { struct ti_clk *clkp = ti_clk_lookup(data->clock_dep[0]); if (clkp != NULL) { /* * We have to wait for the transition to complete * taking a clock away. */ if (wait) { ti_lpsc_module_wait(dev, mod); } ti_clk_put(clkp); } } } static void lpsc_module_get_internal(struct ti_device *dev, struct ti_lpsc_module *module, bool use, bool ret) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); bool modify = false; if (use) { VERBOSE("MODULE_GET: psc_id=%d lpsc_id=%d use_count=%d\n", psc->psc_idx, ti_lpsc_module_idx(dev, module), module->use_count); module->use_count++; if (module->use_count == 1U) { lpsc_module_clk_get(dev, module); modify = true; } } if (ret) { VERBOSE("RETENTION_GET: psc_id=%d lpsc_id=%d ret_count=%d\n", psc->psc_idx, ti_lpsc_module_idx(dev, module), module->ret_count); module->ret_count++; if (module->ret_count == 1U) { lpsc_module_clk_get(dev, module); modify = true; } } if (modify) { lpsc_module_sync_state(dev, module, false); ti_lpsc_module_wait(dev, module); } } static void lpsc_module_put_internal(struct ti_device *dev, struct ti_lpsc_module *module, bool use, bool ret) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); bool modify = false; if (use) { VERBOSE("MODULE_PUT: psc_id=%d lpsc_id=%d use_count=%d\n", psc->psc_idx, ti_lpsc_module_idx(dev, module), module->use_count); module->use_count--; if (module->use_count == 0U) { modify = true; } } if (ret) { VERBOSE("RETENTION_PUT: psc_id=%d lpsc_id=%d ret_count=%d\n", psc->psc_idx, ti_lpsc_module_idx(dev, module), module->ret_count); module->ret_count--; if (module->ret_count == 0U) { modify = true; } } if (modify) { lpsc_module_sync_state(dev, module, false); if ((module->use_count == 0U) && use) { lpsc_module_clk_put(dev, module, true); } if ((module->ret_count == 0U) && ret) { lpsc_module_clk_put(dev, module, true); } } } void ti_lpsc_module_get(struct ti_device *dev, struct ti_lpsc_module *module) { lpsc_module_get_internal(dev, module, true, false); } void ti_lpsc_module_put(struct ti_device *dev, struct ti_lpsc_module *module) { lpsc_module_put_internal(dev, module, true, false); } void ti_lpsc_module_ret_get(struct ti_device *dev, struct ti_lpsc_module *module) { lpsc_module_get_internal(dev, module, false, true); } void ti_lpsc_module_ret_put(struct ti_device *dev, struct ti_lpsc_module *module) { lpsc_module_put_internal(dev, module, false, true); } /* Drop power up ref counts */ static void psc_pd_drop_pwr_up_ref(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_pd_idx_t idx; for (idx = 0U; idx < psc->pd_count; idx++) { struct ti_psc_pd *pd = psc_idx2pd(psc, idx); if ((pd->pwr_up_enabled) != false) { pd->pwr_up_enabled = false; ti_psc_pd_put(dev, pd); } } } struct ti_device *ti_psc_lookup(ti_psc_idx_t id) { struct ti_device *dev; dev = psc_devs; while (dev != NULL) { const struct ti_psc_drv_data *psc; psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); if ((ti_psc_idx_t) psc->psc_idx == id) { break; } dev = psc->data->next; } return dev; } struct ti_psc_pd *ti_psc_lookup_pd(struct ti_device *dev, ti_pd_idx_t id) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); struct ti_psc_pd *pd = NULL; if ((id < psc->pd_count) && ((psc->pd_data[id].flags & TI_PSC_PD_EXISTS) != 0U)) { pd = psc_idx2pd(psc, id); } return pd; } struct ti_lpsc_module *ti_psc_lookup_lpsc(struct ti_device *dev, ti_pd_idx_t id) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); struct ti_lpsc_module *mod = NULL; if ((id < psc->module_count) && ((psc->mod_data[id].flags & TI_LPSC_MODULE_EXISTS) != 0U)) { mod = psc_idx2mod(psc, id); } return mod; } /** * psc_initialize_pds() - Initialize all the PSC powerdomains. * * This initializes the powerdomains by waiting for them to finish any * active transitions, reading their state, and synchronizing it with the * in memory state. */ static void psc_initialize_pds(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_pd_idx_t idx; struct ti_psc_pd *pd; uint8_t state; /* First pass, find out which domains are on */ for (idx = 0U; idx < psc->pd_count; idx++) { pd = psc_idx2pd(psc, idx); if (0U == (psc->pd_data[idx].flags & TI_PSC_PD_EXISTS)) { continue; } VERBOSE("PD_INIT: psc_id=%d lpsc_id=%d\n", psc->psc_idx, idx); ti_psc_pd_wait(dev, pd); state = (uint8_t) psc_read(dev, psc_reg(PSC_PDSTAT_BASE, (uint32_t) idx)); state &= (uint8_t) PDSTAT_STATE_MASK; /* * Mark a PD as power up in use so we don't power everything * off before PMMC startup is complete */ pd->pwr_up_enabled = ((state == PDCTL_STATE_ON) || ((psc->pd_data[idx].flags & TI_PSC_PD_ALWAYSON) != 0U)); } /* Second pass, sync use count and impossible hardware states */ for (idx = 0U; idx < psc->pd_count; idx++) { pd = psc_idx2pd(psc, idx); if (pd->pwr_up_enabled != false) { ti_psc_pd_get(dev, pd); } } } /** * psc_uninitialize_pds() - Unititialize all the power domains of a PSC. * @dev: The device associated with this PSC. * * This sets all the power domains in a PSC to a pre-initialized state * in preparation for the reset of it's reset domain. Because power * domains can only have dependencies on other domains within the same * PSC, this just means ensuring that the clock references are dropped. */ static void psc_uninitialize_pds(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_pd_idx_t idx; for (idx = 0U; idx < psc->pd_count; idx++) { struct ti_psc_pd *pd = psc_idx2pd(psc, idx); if (((psc->pd_data[idx].flags & TI_PSC_PD_EXISTS) != 0U) && (pd->use_count != 0U) && ((psc->pd_data[idx].flags & TI_PSC_PD_ALWAYSON) == 0U)) { psc_pd_clk_put(&psc->pd_data[idx]); } pd->use_count = 0U; pd->pwr_up_enabled = false; } psc->data->pds_enabled = 0U; } /* Drop power up ref counts */ void ti_psc_drop_pwr_up_ref(void) { struct ti_device *dev = psc_devs; const struct ti_psc_drv_data *psc; ti_lpsc_idx_t idx; while (dev != NULL) { psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); for (idx = 0U; idx < psc->module_count; idx++) { struct ti_lpsc_module *mod = psc_idx2mod(psc, idx); if (mod->pwr_up_enabled != 0U) { mod->pwr_up_enabled = 0U; ti_lpsc_module_put(dev, mod); } if (mod->pwr_up_ret != 0U) { mod->pwr_up_ret = 0U; ti_lpsc_module_ret_put(dev, mod); } } dev = psc->data->next; } } /** * psc_initialize_modules() - Initialize all the PSC modules. * * This initializes the modules by waiting for them to finish any active * transitions and reading their state. */ static void psc_initialize_modules(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_lpsc_idx_t idx; /* First pass, find out which modules are enabled */ for (idx = 0U; idx < psc->module_count; idx++) { struct ti_lpsc_module *mod = psc_idx2mod(psc, idx); uint32_t val; uint32_t retry_count; if (0U == (psc->mod_data[idx].flags & TI_LPSC_MODULE_EXISTS)) { continue; } retry_count = PSC_TRANSITION_RETRY_COUNT; while ((((psc_read(dev, psc_reg(PSC_MDSTAT_BASE, (uint32_t) idx))) & MDSTAT_BUSY_MASK) != 0U) && (retry_count > 0U)) { udelay(PSC_TRANSITION_DELAY_US); retry_count--; } if (retry_count == 0U) { VERBOSE("PSC module busy timeout: psc_id=%d lpsc_id=%d\n", psc->psc_idx, idx); } val = psc_read(dev, psc_reg(PSC_MDSTAT_BASE, (uint32_t) idx)); val &= MDSTAT_STATE_MASK; /* Ref count as if we are moving out of off state */ mod->sw_state = MDSTAT_STATE_SWRSTDISABLE; mod->sw_mrst_ret = false; if ((val == MDSTAT_STATE_ENABLE) || (val == MDSTAT_STATE_SYNCRST)) { mod->pwr_up_enabled = 1U; mod->pwr_up_ret = 1U; } else if (val == MDSTAT_STATE_DISABLE) { mod->pwr_up_enabled = 0U; mod->pwr_up_ret = 1U; } else if (val == MDSTAT_STATE_SWRSTDISABLE) { mod->pwr_up_enabled = 0U; mod->pwr_up_ret = 0U; } else { /* Invalid initial state, try turning everything on */ mod->pwr_up_ret = 1U; mod->pwr_up_enabled = 1U; } } } /** * psc_initialize_modules_finish() - Finish initializing all the PSC modules. * * This finishes the initialization of modules by synchronizing their * state with the in memory state. */ static void psc_initialize_modules_finish(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_lpsc_idx_t idx; /* Second pass, sync ref counts */ for (idx = 0U; idx < psc->module_count; idx++) { struct ti_lpsc_module *mod = psc_idx2mod(psc, idx); lpsc_module_get_internal(dev, mod, (mod->pwr_up_enabled != 0U), (mod->pwr_up_ret != 0U)); } psc_pd_drop_pwr_up_ref(dev); } /** * psc_uninitialize_modules() - Unititialize all the LPSC modules of a PSC. * @dev: The device associated with this PSC. * * This sets all the LPSC modules in a PSC to a pre-initialized state * in preparation for the reset of it's reset domain. Because LPSC * modules can have dependencies on other domains, those dependencies * need to be droppe as appropriate. Any clock dependencies are also * dropped. */ static void psc_uninitialize_modules(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); ti_lpsc_idx_t idx; /* First pass, find out which modules are enabled */ for (idx = 0U; idx < psc->module_count; idx++) { struct ti_lpsc_module *mod = psc_idx2mod(psc, idx); if ((psc->mod_data[idx].flags & TI_LPSC_MODULE_EXISTS) == 0U) { continue; } if (mod->use_count != 0U) { lpsc_module_clk_put(dev, mod, false); } if (mod->ret_count != 0U) { lpsc_module_clk_put(dev, mod, false); } mod->use_count = 0U; mod->ret_count = 0U; mod->pwr_up_enabled = 0U; mod->pwr_up_ret = 0U; lpsc_module_sync_state(dev, mod, true); mod->sw_state = MDSTAT_STATE_SWRSTDISABLE; mod->sw_mrst_ret = false; } } /** * psc_module_deps_ready() - Check if PSCs we depend on have completed their initial config * * PSCs can have domains that depend on domains in other PSCs. We break up * initialization into two stages because PSCs can have cross dependencies. * Once all PSCs in a cross dependency set have completed, each one can * move to the second stage of initialization. We use the -EAGAIN system * to manage this. * * Return: 0 on success, <0 on failure. */ static int32_t psc_module_deps_ready(struct ti_device *dev) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); uint32_t idx; ti_psc_idx_t id; for (idx = 0; idx < psc->module_count; idx++) { if (((psc->mod_data[idx].flags & TI_LPSC_DEPENDS) != 0UL) && (psc->mod_data[idx].depends_psc_idx != psc->psc_idx)) { id = (ti_psc_idx_t) psc->mod_data[idx].depends_psc_idx; if (ti_psc_lookup(id) == NULL) { return -EAGAIN; } } } return 0; } static int32_t psc_pre_init(struct ti_device *dev) { const struct ti_drv_data *data; const struct ti_psc_drv_data *psc; data = ti_get_drv_data(dev); psc = ti_to_psc_drv_data(data); /* Only perform initial configuration if it has not been completed */ if (ti_psc_lookup((ti_psc_idx_t) psc->psc_idx) == NULL) { if (psc->base == 0U) { return -EINVAL; } psc_initialize_pds(dev); psc_initialize_modules(dev); /* This marks initial config as complete */ psc->data->next = psc_devs; psc_devs = dev; } return 0; } static int32_t psc_post_init(struct ti_device *dev) { int32_t ret; ret = psc_module_deps_ready(dev); if (ret == 0) { psc_initialize_modules_finish(dev); } return ret; } /** * psc_uninit() - Uninitialize this PSC. * @dev: The device associated with this PSC. * * Perform the steps necessary to bring this PSC back to a pre-init state. * This is performed before a reset domain reset so that the PSC can be * re-initialized after the reset is complete. */ static void psc_uninit(struct ti_device *dev) { const struct ti_psc_drv_data *psc; struct ti_device *curr_dev; struct ti_device **last_dev; psc = ti_to_psc_drv_data(ti_get_drv_data(dev)); psc_uninitialize_modules(dev); psc_uninitialize_pds(dev); /* Remove from list of uninitialized PSCs */ curr_dev = psc_devs; last_dev = &psc_devs; while ((curr_dev != NULL) && (curr_dev != dev)) { const struct ti_psc_drv_data *curr_psc; curr_psc = ti_to_psc_drv_data(ti_get_drv_data(curr_dev)); curr_dev = curr_psc->data->next; last_dev = &curr_psc->data->next; } if (curr_dev == dev) { *last_dev = psc->data->next; psc->data->next = NULL; } } const struct ti_drv psc_drv = { .pre_init = psc_pre_init, .post_init = psc_post_init, .uninit = psc_uninit, };