/* * Copyright (c) 2025-2026 Texas Instruments Incorporated - https://www.ti.com * * SPDX-License-Identifier: BSD-3-Clause */ /* * Device to PSC Module Mapping * * This module bridges devices and PSC LPSC modules, providing state queries, * reset control, and context loss tracking for device-to-PSC module associations. */ #include #include #include #include #include #include /* Sentinel value indicating no LPSC module association */ #define PSC_LPSC_NONE 55U /** * ti_soc_device_get_state_internal() - Get the PSC state for a single SoC device domain. * @dev: SoC device data identifying the PSC index and LPSC module. * * Return: The LPSC module state, or 0 if PSC or module is not found. */ static uint32_t ti_soc_device_get_state_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return 0U; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module == NULL) { return 0U; } return ti_lpsc_module_get_state(psc_dev, module); } uint32_t ti_soc_device_get_state(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t ret = 2U; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { uint32_t this_ret; this_ret = ti_soc_device_get_state_internal(&domains[i]); if (i == 0U) { ret = this_ret; } else if (ret != this_ret) { /* Mixed state of our domains, label as transition */ ret = 2U; } else { /* Do Nothing */ } } } else { ret = ti_soc_device_get_state_internal(&data->soc); } return ret; } /** * ti_soc_device_set_reset_iso_internal() - Set reset isolation for a single SoC device domain. * @dev: SoC device data identifying the PSC index and LPSC module. * @enable: True to enable reset isolation, false to disable. */ static void ti_soc_device_set_reset_iso_internal(const struct ti_soc_device_data *dev, bool enable) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module == NULL) { return; } ti_lpsc_module_set_reset_iso(psc_dev, module, enable); } void ti_soc_device_set_reset_iso(struct ti_device *dev, bool enable) { const struct ti_dev_data *data; assert(dev != NULL); data = ti_get_dev_data(dev); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { /* Reset ISO not supported for devices with multiple domains */ } else { ti_soc_device_set_reset_iso_internal(&data->soc, enable); } } /** * ti_soc_device_get_context_loss_count_internal() - Get context loss count for a device domain. * @dev: SoC device data identifying the PSC index and LPSC module. * * Return: The context loss count for the LPSC module, or 0 if not found. */ static uint32_t ti_soc_device_get_context_loss_count_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return 0U; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module == NULL) { return 0U; } return module->loss_count; } uint32_t ti_soc_device_get_context_loss_count(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t ret = 0U; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ret += ti_soc_device_get_context_loss_count_internal(&domains[i]); } } else { ret = ti_soc_device_get_context_loss_count_internal(&data->soc); } return ret; } /** * ti_soc_device_enable_internal() - Enable the PSC LPSC module for a single SoC device domain. * @dev: SoC device data identifying the PSC index and LPSC module. */ static void ti_soc_device_enable_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module == NULL) { return; } ti_lpsc_module_get(psc_dev, module); } void ti_soc_device_enable(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); VERBOSE("DEVICE_ON: dev_id=%u\n", ti_device_id(dev)); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ti_soc_device_enable_internal(&domains[i]); } } else { ti_soc_device_enable_internal(&data->soc); } } /** * ti_soc_device_disable_internal() - Disable the PSC LPSC module for a single SoC device domain. * @dev: SoC device data identifying the PSC index and LPSC module. */ static void ti_soc_device_disable_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module == NULL) { return; } ti_lpsc_module_put(psc_dev, module); } /** * ti_soc_device_disable_internal_flags_iterate() - Clear all PSC initialization flags. * @psc_dev: The PSC device whose flags are to be cleared. * @module: Entry-point LPSC module (flags cleared here first, then entire PSC reset). * * Clears initialization flags for every power domain and LPSC module in the * PSC referenced by psc_dev. Despite accepting a specific module as the entry * point, the implementation resets the entire PSC state intentionally: all * LPSCs must be disabled before a power domain can power off. Recurses into * any dependency PSC. */ static void ti_soc_device_disable_internal_flags_iterate(struct ti_device *psc_dev, struct ti_lpsc_module *module) { const struct ti_psc_drv_data *psc = ti_to_psc_drv_data(ti_get_drv_data(psc_dev)); const struct ti_lpsc_module_data *data; const struct ti_psc_drv_data *depends_psc; struct ti_device *depends_dev; struct ti_psc_pd *pd; struct ti_lpsc_module *temp; uint32_t idx; struct ti_lpsc_module *module_p = module; if (module_p != NULL) { module_p->use_count = 0U; module_p->ret_count = 0U; module_p->pwr_up_enabled = 0U; module_p->pwr_up_ret = 0U; module_p->sw_state = 0U; module_p->loss_count = 0U; module_p->mrst_active = 0U; module_p->sw_mrst_ret = false; } if (psc == NULL) { return; } for (idx = 0U; idx < psc->pd_count; idx++) { pd = &psc->powerdomains[idx]; pd->use_count = 0U; pd->pwr_up_enabled = false; } for (idx = 0U; idx < psc->module_count; idx++) { temp = &psc->modules[idx]; temp->use_count = 0U; temp->ret_count = 0U; temp->pwr_up_enabled = 0U; temp->pwr_up_ret = 0U; temp->sw_state = 0U; temp->sw_mrst_ret = false; temp->loss_count = 0U; temp->mrst_active = 0U; } psc->data->pds_enabled = 0U; idx = ti_lpsc_module_idx(psc_dev, module_p); data = &psc->mod_data[idx]; if ((data->flags & TI_LPSC_DEPENDS) != 0U) { depends_dev = ti_psc_lookup((ti_psc_idx_t) data->depends_psc_idx); if (depends_dev != NULL) { depends_psc = ti_to_psc_drv_data(ti_get_drv_data(depends_dev)); if ((depends_psc != NULL) && (module_p != NULL)) { module_p = &depends_psc->modules[(ti_lpsc_idx_t) data->depends]; ti_soc_device_disable_internal_flags_iterate(depends_dev, module_p); } } } } /** * ti_soc_device_disable_internal_flags() - Clear PSC initialization flags for a device. * @dev: SoC device data identifying the PSC and LPSC module. * * Clears initialization flags for the PSC module associated with the given device. */ static void ti_soc_device_disable_internal_flags(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; if (psc_dev == NULL) { return; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); ti_soc_device_disable_internal_flags_iterate(psc_dev, module); } void ti_soc_device_disable(struct ti_device *dev, bool domain_reset) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); VERBOSE("DEVICE_OFF: dev_id=%u\n", ti_device_id(dev)); if (domain_reset == false) { if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ti_soc_device_disable_internal(&domains[i]); } } else { ti_soc_device_disable_internal(&data->soc); } } } void ti_soc_device_clear_flags(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); VERBOSE("CLEAR_FLAGS: dev_id=%u\n", ti_device_id(dev)); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ti_soc_device_disable_internal_flags(&domains[i]); } } else { ti_soc_device_disable_internal_flags(&data->soc); } } static void ti_soc_device_ret_enable_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_psc_pd *pd; struct ti_lpsc_module *module; if (psc_dev == NULL) { return; } pd = ti_psc_lookup_pd(psc_dev, (ti_pd_idx_t) dev->pd); if (pd != NULL) { ti_psc_pd_get(psc_dev, pd); } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module != NULL) { ti_lpsc_module_ret_get(psc_dev, module); } } void ti_soc_device_ret_enable(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ti_soc_device_ret_enable_internal(&domains[i]); } } else { ti_soc_device_ret_enable_internal(&data->soc); } } static void ti_soc_device_ret_disable_internal(const struct ti_soc_device_data *dev) { struct ti_device *psc_dev = ti_psc_lookup((ti_psc_idx_t) dev->psc_idx); struct ti_lpsc_module *module; struct ti_psc_pd *pd; if (psc_dev == NULL) { return; } module = ti_psc_lookup_lpsc(psc_dev, dev->mod); if (module != NULL) { ti_lpsc_module_ret_put(psc_dev, module); } pd = ti_psc_lookup_pd(psc_dev, (ti_pd_idx_t) dev->pd); if (pd != NULL) { ti_psc_pd_put(psc_dev, pd); } } void ti_soc_device_ret_disable(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *data; uint32_t i; assert(dev != NULL); data = ti_get_dev_data(dev); if (data->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { domains = soc_psc_multiple_domains[data->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { ti_soc_device_ret_disable_internal(&domains[i]); } } else { ti_soc_device_ret_disable_internal(&data->soc); } } void ti_soc_device_init_complete(void) { ti_psc_drop_pwr_up_ref(); } static int32_t ti_soc_device_verify_mapping(const struct ti_psc_drv_data *psc, uint32_t dev_id, const struct ti_soc_device_data *dev) { const struct ti_lpsc_module_data *mdata; uint32_t i; /* * Make sure the redundant const data stored in the PSC data is * correct. This redundant data would ideally be checked or even * generated at compile time. */ if (dev->mod != PSC_LPSC_NONE) { mdata = &psc->mod_data[dev->mod]; if ((mdata->flags & TI_LPSC_DEVICES_LIST) != 0U) { for (i = 0U; mdata->lpsc_dev.dev_list[i] != dev_id; i++) { if (mdata->lpsc_dev.dev_list[i] == TI_DEV_ID_NONE) { VERBOSE("ACTION FAIL: INVALID_PSC_DATA\n"); return -EINVAL; } } } else { for (i = 0U; mdata->lpsc_dev.dev_array[i] != dev_id; i++) { if ((i >= (ARRAY_SIZE(mdata->lpsc_dev.dev_array) - 1UL)) || (mdata->lpsc_dev.dev_array[i] == TI_DEV_ID_NONE)) { VERBOSE("ACTION FAIL: INVALID_PSC_DATA\n"); return -EINVAL; } } } } return 0; } static int32_t ti_soc_device_init_internal(struct ti_device *dev) { const struct ti_soc_device_data *domains; const struct ti_dev_data *devdata; const struct ti_drv_data *drvdata; struct ti_device *psc_dev = NULL; const struct ti_psc_drv_data *psc = NULL; uint32_t dev_id; int32_t ret; uint32_t i; dev_id = ti_device_id(dev); devdata = ti_get_dev_data(dev); /* Check if this PSC manages its own power domain */ if ((devdata->flags & TI_DEVD_FLAG_DRV_DATA) != 0U) { drvdata = ti_to_drv_data(devdata); if (drvdata->drv == &psc_drv) { psc = ti_to_psc_drv_data(drvdata); if (psc->psc_idx == devdata->soc.psc_idx) { psc_dev = dev; } else { psc = NULL; } } } if (psc_dev != NULL) { /* We are our own PSC */ return ti_soc_device_verify_mapping(psc, dev_id, &devdata->soc); } if (devdata->soc.psc_idx == TI_PSC_DEV_MULTIPLE) { /* Find all the PSCs this device needs */ domains = soc_psc_multiple_domains[devdata->soc.mod]; for (i = 0U; domains[i].psc_idx != TI_PSC_DEV_NONE; i++) { psc_dev = ti_psc_lookup((ti_psc_idx_t) domains[i].psc_idx); if (psc_dev == NULL) { return -EAGAIN; } psc = ti_to_psc_drv_data(ti_get_drv_data(psc_dev)); ret = ti_soc_device_verify_mapping(psc, dev_id, &domains[i]); if (ret != 0) { return ret; } } return 0; } /* We just need the one PSC */ psc_dev = ti_psc_lookup((ti_psc_idx_t) devdata->soc.psc_idx); if (psc_dev == NULL) { /* * Don't try to bring this dev up before calling init * on the device's PSC. */ return -EAGAIN; } psc = ti_to_psc_drv_data(ti_get_drv_data(psc_dev)); return ti_soc_device_verify_mapping(psc, dev_id, &devdata->soc); } /* Defer all other device initialization until PSC initializes */ int32_t ti_soc_device_init(struct ti_device *dev) { const struct ti_dev_data *devdata; assert(dev != NULL); devdata = ti_get_dev_data(dev); if (devdata->soc.psc_idx != TI_PSC_DEV_NONE) { return ti_soc_device_init_internal(dev); } return 0; }