Commit a070a08d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pmdomain-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm

Pull pmdomain updates from Ulf Hansson:
 "Core:
   - Log a message when unused PM domains gets disabled
   - Scale down parent/child performance states in the reverse order

  Providers:
   - qcom: rpmpd: Add power domains support for MSM8974, MSM8974PRO,
     PMA8084 and PM8841
   - renesas: rcar-gen4-sysc: Reduce atomic delays
   - renesas: rcar-sysc: Adjust the waiting time to cover the worst case
   - renesas: r8a779h0-sysc: Add support for the r8a779h0 PM domains
   - imx: imx8mp-blk-ctrl: Add the fdcc clock to the hdmimix domains
   - imx: imx8mp-blk-ctrl: Error out if domains are missing in DT

  Improve support for multiple PM domains:
   - Add two helper functions to attach/detach multiple PM domains
   - Convert a couple of drivers to use the new helper functions"

* tag 'pmdomain-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm: (22 commits)
  pmdomain: renesas: rcar-gen4-sysc: Reduce atomic delays
  pmdomain: renesas: Adjust the waiting time to cover the worst case
  pmdomain: qcom: rpmpd: Add MSM8974PRO+PMA8084 power domains
  pmdomain: qcom: rpmpd: Add MSM8974+PM8841 power domains
  pmdomain: core: constify of_phandle_args in add device and subdomain
  pmdomain: core: constify of_phandle_args in xlate
  media: venus: Convert to dev_pm_domain_attach|detach_list() for vcodec
  remoteproc: qcom_q6v5_adsp: Convert to dev_pm_domain_attach|detach_list()
  remoteproc: imx_rproc: Convert to dev_pm_domain_attach|detach_list()
  remoteproc: imx_dsp_rproc: Convert to dev_pm_domain_attach|detach_list()
  PM: domains: Add helper functions to attach/detach multiple PM domains
  pmdomain: imx8mp-blk-ctrl: imx8mp_blk: Add fdcc clock to hdmimix domain
  pmdomain: mediatek: Use devm_platform_ioremap_resource() in init_scp()
  pmdomain: renesas: r8a779h0-sysc: Add r8a779h0 support
  pmdomain: imx8mp-blk-ctrl: Error out if domains are missing in DT
  pmdomain: ti: Add a null pointer check to the omap_prm_domain_init
  pmdomain: renesas: rcar-gen4-sysc: Remove unneeded includes
  pmdomain: core: Print a message when unused power domains are disabled
  pmdomain: qcom: rpmpd: Keep one RPM handle for all RPMPDs
  pmdomain: core: Scale down parent/child performance states in reverse order
  ...
parents 15223fdb ccabbb67
......@@ -24,6 +24,8 @@ properties:
- qcom,msm8917-rpmpd
- qcom,msm8939-rpmpd
- qcom,msm8953-rpmpd
- qcom,msm8974-rpmpd
- qcom,msm8974pro-pma8084-rpmpd
- qcom,msm8976-rpmpd
- qcom,msm8994-rpmpd
- qcom,msm8996-rpmpd
......
......@@ -27,8 +27,8 @@ properties:
const: 1
power-domains:
minItems: 8
maxItems: 8
minItems: 10
maxItems: 10
power-domain-names:
items:
......@@ -40,10 +40,12 @@ properties:
- const: trng
- const: hdmi-tx
- const: hdmi-tx-phy
- const: hdcp
- const: hrv
clocks:
minItems: 4
maxItems: 4
minItems: 5
maxItems: 5
clock-names:
items:
......@@ -51,6 +53,7 @@ properties:
- const: axi
- const: ref_266m
- const: ref_24m
- const: fdcc
interconnects:
maxItems: 3
......@@ -82,12 +85,15 @@ examples:
clocks = <&clk IMX8MP_CLK_HDMI_APB>,
<&clk IMX8MP_CLK_HDMI_ROOT>,
<&clk IMX8MP_CLK_HDMI_REF_266M>,
<&clk IMX8MP_CLK_HDMI_24M>;
clock-names = "apb", "axi", "ref_266m", "ref_24m";
<&clk IMX8MP_CLK_HDMI_24M>,
<&clk IMX8MP_CLK_HDMI_FDCC_TST>;
clock-names = "apb", "axi", "ref_266m", "ref_24m", "fdcc";
power-domains = <&pgc_hdmimix>, <&pgc_hdmimix>, <&pgc_hdmimix>,
<&pgc_hdmimix>, <&pgc_hdmimix>, <&pgc_hdmimix>,
<&pgc_hdmimix>, <&pgc_hdmi_phy>;
<&pgc_hdmimix>, <&pgc_hdmi_phy>,
<&pgc_hdmimix>, <&pgc_hdmimix>;
power-domain-names = "bus", "irqsteer", "lcdif", "pai", "pvi", "trng",
"hdmi-tx", "hdmi-tx-phy";
"hdmi-tx", "hdmi-tx-phy",
"hdcp", "hrv";
#power-domain-cells = <1>;
};
......@@ -167,6 +167,115 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev,
}
EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_name);
/**
* dev_pm_domain_attach_list - Associate a device with its PM domains.
* @dev: The device used to lookup the PM domains for.
* @data: The data used for attaching to the PM domains.
* @list: An out-parameter with an allocated list of attached PM domains.
*
* This function helps to attach a device to its multiple PM domains. The
* caller, which is typically a driver's probe function, may provide a list of
* names for the PM domains that we should try to attach the device to, but it
* may also provide an empty list, in case the attach should be done for all of
* the available PM domains.
*
* Callers must ensure proper synchronization of this function with power
* management callbacks.
*
* Returns the number of attached PM domains or a negative error code in case of
* a failure. Note that, to detach the list of PM domains, the driver shall call
* dev_pm_domain_detach_list(), typically during the remove phase.
*/
int dev_pm_domain_attach_list(struct device *dev,
const struct dev_pm_domain_attach_data *data,
struct dev_pm_domain_list **list)
{
struct device_node *np = dev->of_node;
struct dev_pm_domain_list *pds;
struct device *pd_dev = NULL;
int ret, i, num_pds = 0;
bool by_id = true;
u32 pd_flags = data ? data->pd_flags : 0;
u32 link_flags = pd_flags & PD_FLAG_NO_DEV_LINK ? 0 :
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
if (dev->pm_domain)
return -EEXIST;
/* For now this is limited to OF based platforms. */
if (!np)
return 0;
if (data && data->pd_names) {
num_pds = data->num_pd_names;
by_id = false;
} else {
num_pds = of_count_phandle_with_args(np, "power-domains",
"#power-domain-cells");
}
if (num_pds <= 0)
return 0;
pds = devm_kzalloc(dev, sizeof(*pds), GFP_KERNEL);
if (!pds)
return -ENOMEM;
pds->pd_devs = devm_kcalloc(dev, num_pds, sizeof(*pds->pd_devs),
GFP_KERNEL);
if (!pds->pd_devs)
return -ENOMEM;
pds->pd_links = devm_kcalloc(dev, num_pds, sizeof(*pds->pd_links),
GFP_KERNEL);
if (!pds->pd_links)
return -ENOMEM;
if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON)
link_flags |= DL_FLAG_RPM_ACTIVE;
for (i = 0; i < num_pds; i++) {
if (by_id)
pd_dev = dev_pm_domain_attach_by_id(dev, i);
else
pd_dev = dev_pm_domain_attach_by_name(dev,
data->pd_names[i]);
if (IS_ERR_OR_NULL(pd_dev)) {
ret = pd_dev ? PTR_ERR(pd_dev) : -ENODEV;
goto err_attach;
}
if (link_flags) {
struct device_link *link;
link = device_link_add(dev, pd_dev, link_flags);
if (!link) {
ret = -ENODEV;
goto err_link;
}
pds->pd_links[i] = link;
}
pds->pd_devs[i] = pd_dev;
}
pds->num_pds = num_pds;
*list = pds;
return num_pds;
err_link:
dev_pm_domain_detach(pd_dev, true);
err_attach:
while (--i >= 0) {
if (pds->pd_links[i])
device_link_del(pds->pd_links[i]);
dev_pm_domain_detach(pds->pd_devs[i], true);
}
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_domain_attach_list);
/**
* dev_pm_domain_detach - Detach a device from its PM domain.
* @dev: Device to detach.
......@@ -187,6 +296,31 @@ void dev_pm_domain_detach(struct device *dev, bool power_off)
}
EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
/**
* dev_pm_domain_detach_list - Detach a list of PM domains.
* @list: The list of PM domains to detach.
*
* This function reverse the actions from dev_pm_domain_attach_list().
* Typically it should be invoked during the remove phase from drivers.
*
* Callers must ensure proper synchronization of this function with power
* management callbacks.
*/
void dev_pm_domain_detach_list(struct dev_pm_domain_list *list)
{
int i;
if (!list)
return;
for (i = 0; i < list->num_pds; i++) {
if (list->pd_links[i])
device_link_del(list->pd_links[i]);
dev_pm_domain_detach(list->pd_devs[i], true);
}
}
EXPORT_SYMBOL_GPL(dev_pm_domain_detach_list);
/**
* dev_pm_domain_start - Start the device through its PM domain.
* @dev: Device to start.
......
......@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-mem2mem.h>
......@@ -114,7 +115,8 @@ static void venus_sys_error_handler(struct work_struct *work)
pm_runtime_put_sync(core->dev);
for (i = 0; i < max_attempts; i++) {
if (!core->pmdomains[0] || !pm_runtime_active(core->pmdomains[0]))
if (!core->pmdomains ||
!pm_runtime_active(core->pmdomains->pd_devs[0]))
break;
usleep_range(1000, 1500);
}
......@@ -705,7 +707,7 @@ static const struct venus_resources sdm845_res_v2 = {
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
.vcodec1_clks = { "vcodec1_core", "vcodec1_bus" },
.vcodec_clks_num = 2,
.vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" },
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" },
.vcodec_pmdomains_num = 3,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 2,
......@@ -754,7 +756,7 @@ static const struct venus_resources sc7180_res = {
.clks_num = 3,
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
.vcodec_clks_num = 2,
.vcodec_pmdomains = { "venus", "vcodec0" },
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 1,
......@@ -811,7 +813,7 @@ static const struct venus_resources sm8250_res = {
.resets_num = 2,
.vcodec0_clks = { "vcodec0_core" },
.vcodec_clks_num = 1,
.vcodec_pmdomains = { "venus", "vcodec0" },
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "mx", NULL },
.vcodec_num = 1,
......@@ -870,7 +872,7 @@ static const struct venus_resources sc7280_res = {
.clks_num = 3,
.vcodec0_clks = {"vcodec_core", "vcodec_bus"},
.vcodec_clks_num = 2,
.vcodec_pmdomains = { "venus", "vcodec0" },
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 1,
......
......@@ -25,7 +25,6 @@
#define VIDC_CLKS_NUM_MAX 4
#define VIDC_VCODEC_CLKS_NUM_MAX 2
#define VIDC_PMDOMAINS_NUM_MAX 3
#define VIDC_RESETS_NUM_MAX 2
extern int venus_fw_debug;
......@@ -72,7 +71,7 @@ struct venus_resources {
const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
unsigned int vcodec_clks_num;
const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX];
const char **vcodec_pmdomains;
unsigned int vcodec_pmdomains_num;
const char **opp_pmdomain;
unsigned int vcodec_num;
......@@ -134,7 +133,7 @@ struct venus_format {
* @video_path: an interconnect handle to video to/from memory path
* @cpucfg_path: an interconnect handle to cpu configuration path
* @has_opp_table: does OPP table exist
* @pmdomains: an array of pmdomains struct device pointers
* @pmdomains: a pointer to a list of pmdomains
* @opp_dl_venus: an device-link for device OPP
* @opp_pmdomain: an OPP power-domain
* @resets: an array of reset signals
......@@ -187,7 +186,7 @@ struct venus_core {
struct icc_path *video_path;
struct icc_path *cpucfg_path;
bool has_opp_table;
struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX];
struct dev_pm_domain_list *pmdomains;
struct device_link *opp_dl_venus;
struct device *opp_pmdomain;
struct reset_control *resets[VIDC_RESETS_NUM_MAX];
......
......@@ -455,7 +455,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
if (ret)
return ret;
ret = pm_runtime_put_sync(core->pmdomains[1]);
ret = pm_runtime_put_sync(core->pmdomains->pd_devs[1]);
if (ret < 0)
return ret;
}
......@@ -471,7 +471,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
if (ret)
return ret;
ret = pm_runtime_put_sync(core->pmdomains[2]);
ret = pm_runtime_put_sync(core->pmdomains->pd_devs[2]);
if (ret < 0)
return ret;
}
......@@ -484,7 +484,7 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
int ret;
if (coreid_mask & VIDC_CORE_ID_1) {
ret = pm_runtime_get_sync(core->pmdomains[1]);
ret = pm_runtime_get_sync(core->pmdomains->pd_devs[1]);
if (ret < 0)
return ret;
......@@ -502,7 +502,7 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
}
if (coreid_mask & VIDC_CORE_ID_2) {
ret = pm_runtime_get_sync(core->pmdomains[2]);
ret = pm_runtime_get_sync(core->pmdomains->pd_devs[2]);
if (ret < 0)
return ret;
......@@ -860,19 +860,18 @@ static int vcodec_domains_get(struct venus_core *core)
struct device **opp_virt_dev;
struct device *dev = core->dev;
const struct venus_resources *res = core->res;
struct device *pd;
unsigned int i;
struct dev_pm_domain_attach_data vcodec_data = {
.pd_names = res->vcodec_pmdomains,
.num_pd_names = res->vcodec_pmdomains_num,
.pd_flags = PD_FLAG_NO_DEV_LINK,
};
if (!res->vcodec_pmdomains_num)
goto skip_pmdomains;
for (i = 0; i < res->vcodec_pmdomains_num; i++) {
pd = dev_pm_domain_attach_by_name(dev,
res->vcodec_pmdomains[i]);
if (IS_ERR_OR_NULL(pd))
return pd ? PTR_ERR(pd) : -ENODATA;
core->pmdomains[i] = pd;
}
ret = dev_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains);
if (ret < 0)
return ret;
skip_pmdomains:
if (!core->res->opp_pmdomain)
......@@ -896,30 +895,14 @@ static int vcodec_domains_get(struct venus_core *core)
return 0;
opp_attach_err:
for (i = 0; i < res->vcodec_pmdomains_num; i++) {
if (IS_ERR_OR_NULL(core->pmdomains[i]))
continue;
dev_pm_domain_detach(core->pmdomains[i], true);
}
dev_pm_domain_detach_list(core->pmdomains);
return ret;
}
static void vcodec_domains_put(struct venus_core *core)
{
const struct venus_resources *res = core->res;
unsigned int i;
dev_pm_domain_detach_list(core->pmdomains);
if (!res->vcodec_pmdomains_num)
goto skip_pmdomains;
for (i = 0; i < res->vcodec_pmdomains_num; i++) {
if (IS_ERR_OR_NULL(core->pmdomains[i]))
continue;
dev_pm_domain_detach(core->pmdomains[i], true);
}
skip_pmdomains:
if (!core->has_opp_table)
return;
......@@ -1035,7 +1018,8 @@ static void core_put_v4(struct venus_core *core)
static int core_power_v4(struct venus_core *core, int on)
{
struct device *dev = core->dev;
struct device *pmctrl = core->pmdomains[0];
struct device *pmctrl = core->pmdomains ?
core->pmdomains->pd_devs[0] : NULL;
int ret = 0;
if (on == POWER_ON) {
......
......@@ -311,72 +311,102 @@ static int genpd_xlate_performance_state(struct generic_pm_domain *genpd,
}
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
unsigned int state, int depth)
unsigned int state, int depth);
static void _genpd_rollback_parent_state(struct gpd_link *link, int depth)
{
struct generic_pm_domain *parent;
struct gpd_link *link;
int parent_state, ret;
struct generic_pm_domain *parent = link->parent;
int parent_state;
if (state == genpd->performance_state)
return 0;
genpd_lock_nested(parent, depth + 1);
/* Propagate to parents of genpd */
list_for_each_entry(link, &genpd->child_links, child_node) {
parent = link->parent;
parent_state = link->prev_performance_state;
link->performance_state = parent_state;
/* Find parent's performance state */
ret = genpd_xlate_performance_state(genpd, parent, state);
if (unlikely(ret < 0))
goto err;
parent_state = _genpd_reeval_performance_state(parent, parent_state);
if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
pr_err("%s: Failed to roll back to %d performance state\n",
parent->name, parent_state);
}
parent_state = ret;
genpd_unlock(parent);
}
genpd_lock_nested(parent, depth + 1);
static int _genpd_set_parent_state(struct generic_pm_domain *genpd,
struct gpd_link *link,
unsigned int state, int depth)
{
struct generic_pm_domain *parent = link->parent;
int parent_state, ret;
link->prev_performance_state = link->performance_state;
link->performance_state = parent_state;
parent_state = _genpd_reeval_performance_state(parent,
parent_state);
ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
if (ret)
link->performance_state = link->prev_performance_state;
/* Find parent's performance state */
ret = genpd_xlate_performance_state(genpd, parent, state);
if (unlikely(ret < 0))
return ret;
genpd_unlock(parent);
parent_state = ret;
if (ret)
goto err;
}
genpd_lock_nested(parent, depth + 1);
if (genpd->set_performance_state) {
ret = genpd->set_performance_state(genpd, state);
if (ret)
goto err;
}
link->prev_performance_state = link->performance_state;
link->performance_state = parent_state;
genpd->performance_state = state;
return 0;
parent_state = _genpd_reeval_performance_state(parent, parent_state);
ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
if (ret)
link->performance_state = link->prev_performance_state;
err:
/* Encountered an error, lets rollback */
list_for_each_entry_continue_reverse(link, &genpd->child_links,
child_node) {
parent = link->parent;
genpd_unlock(parent);
genpd_lock_nested(parent, depth + 1);
return ret;
}
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
unsigned int state, int depth)
{
struct gpd_link *link = NULL;
int ret;
if (state == genpd->performance_state)
return 0;
parent_state = link->prev_performance_state;
link->performance_state = parent_state;
/* When scaling up, propagate to parents first in normal order */
if (state > genpd->performance_state) {
list_for_each_entry(link, &genpd->child_links, child_node) {
ret = _genpd_set_parent_state(genpd, link, state, depth);
if (ret)
goto rollback_parents_up;
}
}
parent_state = _genpd_reeval_performance_state(parent,
parent_state);
if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
pr_err("%s: Failed to roll back to %d performance state\n",
parent->name, parent_state);
if (genpd->set_performance_state) {
ret = genpd->set_performance_state(genpd, state);
if (ret) {
if (link)
goto rollback_parents_up;
return ret;
}
}
genpd_unlock(parent);
/* When scaling down, propagate to parents last in reverse order */
if (state < genpd->performance_state) {
list_for_each_entry_reverse(link, &genpd->child_links, child_node) {
ret = _genpd_set_parent_state(genpd, link, state, depth);
if (ret)
goto rollback_parents_down;
}
}
genpd->performance_state = state;
return 0;
rollback_parents_up:
list_for_each_entry_continue_reverse(link, &genpd->child_links, child_node)
_genpd_rollback_parent_state(link, depth);
return ret;
rollback_parents_down:
list_for_each_entry_continue(link, &genpd->child_links, child_node)
_genpd_rollback_parent_state(link, depth);
return ret;
}
......@@ -1100,6 +1130,7 @@ static int __init genpd_power_off_unused(void)
return 0;
}
pr_info("genpd: Disabling unused power domains\n");
mutex_lock(&gpd_list_lock);
list_for_each_entry(genpd, &gpd_list, gpd_list_node)
......@@ -2235,7 +2266,7 @@ static DEFINE_MUTEX(of_genpd_mutex);
* to be a valid pointer to struct generic_pm_domain.
*/
static struct generic_pm_domain *genpd_xlate_simple(
struct of_phandle_args *genpdspec,
const struct of_phandle_args *genpdspec,
void *data)
{
return data;
......@@ -2252,7 +2283,7 @@ static struct generic_pm_domain *genpd_xlate_simple(
* the genpd_onecell_data struct when registering the provider.
*/
static struct generic_pm_domain *genpd_xlate_onecell(
struct of_phandle_args *genpdspec,
const struct of_phandle_args *genpdspec,
void *data)
{
struct genpd_onecell_data *genpd_data = data;
......@@ -2495,7 +2526,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
* on failure.
*/
static struct generic_pm_domain *genpd_get_from_provider(
struct of_phandle_args *genpdspec)
const struct of_phandle_args *genpdspec)
{
struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
struct of_genpd_provider *provider;
......@@ -2526,7 +2557,7 @@ static struct generic_pm_domain *genpd_get_from_provider(
* Looks-up an I/O PM domain based upon phandle args provided and adds
* the device to the PM domain. Returns a negative error code on failure.
*/
int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
int of_genpd_add_device(const struct of_phandle_args *genpdspec, struct device *dev)
{
struct generic_pm_domain *genpd;
int ret;
......@@ -2560,8 +2591,8 @@ EXPORT_SYMBOL_GPL(of_genpd_add_device);
* provided and adds the subdomain to the parent PM domain. Returns a
* negative error code on failure.
*/
int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec)
{
struct generic_pm_domain *parent, *subdomain;
int ret;
......@@ -2598,8 +2629,8 @@ EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
* provided and removes the subdomain from the parent PM domain. Returns a
* negative error code on failure.
*/
int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec)
{
struct generic_pm_domain *parent, *subdomain;
int ret;
......
......@@ -258,11 +258,14 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
domain->power_dev =
dev_pm_domain_attach_by_name(dev, data->gpc_name);
if (IS_ERR(domain->power_dev)) {
dev_err_probe(dev, PTR_ERR(domain->power_dev),
if (IS_ERR_OR_NULL(domain->power_dev)) {
if (!domain->power_dev)
ret = -ENODEV;
else
ret = PTR_ERR(domain->power_dev);
dev_err_probe(dev, ret,
"failed to attach power domain \"%s\"\n",
data->gpc_name);
ret = PTR_ERR(domain->power_dev);
goto cleanup_pds;
}
......
......@@ -55,7 +55,7 @@ struct imx8mp_blk_ctrl_domain_data {
const char *gpc_name;
};
#define DOMAIN_MAX_CLKS 2
#define DOMAIN_MAX_CLKS 3
#define DOMAIN_MAX_PATHS 3
struct imx8mp_blk_ctrl_domain {
......@@ -457,8 +457,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = {
},
[IMX8MP_HDMIBLK_PD_LCDIF] = {
.name = "hdmiblk-lcdif",
.clk_names = (const char *[]){ "axi", "apb" },
.num_clks = 2,
.clk_names = (const char *[]){ "axi", "apb", "fdcc" },
.num_clks = 3,
.gpc_name = "lcdif",
.path_names = (const char *[]){"lcdif-hdmi"},
.num_paths = 1,
......@@ -483,8 +483,8 @@ static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = {
},
[IMX8MP_HDMIBLK_PD_HDMI_TX] = {
.name = "hdmiblk-hdmi-tx",
.clk_names = (const char *[]){ "apb", "ref_266m" },
.num_clks = 2,
.clk_names = (const char *[]){ "apb", "ref_266m", "fdcc" },
.num_clks = 3,
.gpc_name = "hdmi-tx",
},
[IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = {
......@@ -687,11 +687,14 @@ static int imx8mp_blk_ctrl_probe(struct platform_device *pdev)
domain->power_dev =
dev_pm_domain_attach_by_name(dev, data->gpc_name);
if (IS_ERR(domain->power_dev)) {
dev_err_probe(dev, PTR_ERR(domain->power_dev),
if (IS_ERR_OR_NULL(domain->power_dev)) {
if (!domain->power_dev)
ret = -ENODEV;
else
ret = PTR_ERR(domain->power_dev);
dev_err_probe(dev, ret,
"failed to attach power domain %s\n",
data->gpc_name);
ret = PTR_ERR(domain->power_dev);
goto cleanup_pds;
}
......
......@@ -393,7 +393,7 @@ static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
return imx_sc_pd_power(domain, false);
}
static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec,
static struct generic_pm_domain *imx_scu_pd_xlate(const struct of_phandle_args *spec,
void *data)
{
struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
......
......@@ -425,7 +425,6 @@ static struct scp *init_scp(struct platform_device *pdev,
bool bus_prot_reg_update)
{
struct genpd_onecell_data *pd_data;
struct resource *res;
int i, j;
struct scp *scp;
struct clk *clk[CLK_MAX];
......@@ -441,8 +440,7 @@ static struct scp *init_scp(struct platform_device *pdev,
scp->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
scp->base = devm_ioremap_resource(&pdev->dev, res);
scp->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(scp->base))
return ERR_CAST(scp->base);
......
......@@ -16,6 +16,8 @@
#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd)
static struct qcom_smd_rpm *rpmpd_smd_rpm;
/* Resource types:
* RPMPD_X is X encoded as a little-endian, lower-case, ASCII string */
#define RPMPD_SMPA 0x61706d73
......@@ -54,7 +56,6 @@ struct rpmpd {
bool enabled;
const int res_type;
const int res_id;
struct qcom_smd_rpm *rpm;
unsigned int max_state;
__le32 key;
bool state_synced;
......@@ -226,7 +227,46 @@ static struct rpmpd cx_s3a_vfl = {
.key = KEY_FLOOR_LEVEL,
};
static struct rpmpd cx_s2b_corner_ao;
static struct rpmpd cx_s2b_corner = {
.pd = { .name = "cx", },
.peer = &cx_s2b_corner_ao,
.res_type = RPMPD_SMPB,
.res_id = 2,
.key = KEY_CORNER,
};
static struct rpmpd cx_s2b_corner_ao = {
.pd = { .name = "cx_ao", },
.peer = &cx_s2b_corner,
.active_only = true,
.res_type = RPMPD_SMPB,
.res_id = 2,
.key = KEY_CORNER,
};
static struct rpmpd cx_s2b_vfc = {
.pd = { .name = "cx_vfc", },
.res_type = RPMPD_SMPB,
.res_id = 2,
.key = KEY_FLOOR_CORNER,
};
/* G(F)X */
static struct rpmpd gfx_s7a_corner = {
.pd = { .name = "gfx", },
.res_type = RPMPD_SMPA,
.res_id = 7,
.key = KEY_CORNER,
};
static struct rpmpd gfx_s7a_vfc = {
.pd = { .name = "gfx_vfc", },
.res_type = RPMPD_SMPA,
.res_id = 7,
.key = KEY_FLOOR_CORNER,
};
static struct rpmpd gfx_s2b_corner = {
.pd = { .name = "gfx", },
.res_type = RPMPD_SMPB,
......@@ -241,6 +281,20 @@ static struct rpmpd gfx_s2b_vfc = {
.key = KEY_FLOOR_CORNER,
};
static struct rpmpd gfx_s4b_corner = {
.pd = { .name = "gfx", },
.res_type = RPMPD_SMPB,
.res_id = 4,
.key = KEY_CORNER,
};
static struct rpmpd gfx_s4b_vfc = {
.pd = { .name = "gfx_vfc", },
.res_type = RPMPD_SMPB,
.res_id = 4,
.key = KEY_FLOOR_CORNER,
};
static struct rpmpd mx_rwmx0_lvl;
static struct rpmpd gx_rwgx0_lvl_ao;
static struct rpmpd gx_rwgx0_lvl = {
......@@ -663,6 +717,34 @@ static const struct rpmpd_desc msm8953_desc = {
.max_state = RPM_SMD_LEVEL_TURBO,
};
static struct rpmpd *msm8974_rpmpds[] = {
[MSM8974_VDDCX] = &cx_s2b_corner,
[MSM8974_VDDCX_AO] = &cx_s2b_corner_ao,
[MSM8974_VDDCX_VFC] = &cx_s2b_vfc,
[MSM8974_VDDGFX] = &gfx_s4b_corner,
[MSM8974_VDDGFX_VFC] = &gfx_s4b_vfc,
};
static const struct rpmpd_desc msm8974_desc = {
.rpmpds = msm8974_rpmpds,
.num_pds = ARRAY_SIZE(msm8974_rpmpds),
.max_state = MAX_CORNER_RPMPD_STATE,
};
static struct rpmpd *msm8974pro_pma8084_rpmpds[] = {
[MSM8974_VDDCX] = &cx_s2a_corner,
[MSM8974_VDDCX_AO] = &cx_s2a_corner_ao,
[MSM8974_VDDCX_VFC] = &cx_s2a_vfc,
[MSM8974_VDDGFX] = &gfx_s7a_corner,
[MSM8974_VDDGFX_VFC] = &gfx_s7a_vfc,
};
static const struct rpmpd_desc msm8974pro_pma8084_desc = {
.rpmpds = msm8974pro_pma8084_rpmpds,
.num_pds = ARRAY_SIZE(msm8974pro_pma8084_rpmpds),
.max_state = MAX_CORNER_RPMPD_STATE,
};
static struct rpmpd *msm8976_rpmpds[] = {
[MSM8976_VDDCX] = &cx_s2a_lvl,
[MSM8976_VDDCX_AO] = &cx_s2a_lvl_ao,
......@@ -856,6 +938,8 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,msm8917-rpmpd", .data = &msm8917_desc },
{ .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc },
{ .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc },
{ .compatible = "qcom,msm8974-rpmpd", .data = &msm8974_desc },
{ .compatible = "qcom,msm8974pro-pma8084-rpmpd", .data = &msm8974pro_pma8084_desc },
{ .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc },
{ .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc },
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
......@@ -879,7 +963,7 @@ static int rpmpd_send_enable(struct rpmpd *pd, bool enable)
.value = cpu_to_le32(enable),
};
return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
return qcom_rpm_smd_write(rpmpd_smd_rpm, QCOM_SMD_RPM_ACTIVE_STATE,
pd->res_type, pd->res_id, &req, sizeof(req));
}
......@@ -891,7 +975,7 @@ static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner)
.value = cpu_to_le32(corner),
};
return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id,
return qcom_rpm_smd_write(rpmpd_smd_rpm, state, pd->res_type, pd->res_id,
&req, sizeof(req));
};
......@@ -1004,12 +1088,11 @@ static int rpmpd_probe(struct platform_device *pdev)
int i;
size_t num;
struct genpd_onecell_data *data;
struct qcom_smd_rpm *rpm;
struct rpmpd **rpmpds;
const struct rpmpd_desc *desc;
rpm = dev_get_drvdata(pdev->dev.parent);
if (!rpm) {
rpmpd_smd_rpm = dev_get_drvdata(pdev->dev.parent);
if (!rpmpd_smd_rpm) {
dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
return -ENODEV;
}
......@@ -1039,7 +1122,6 @@ static int rpmpd_probe(struct platform_device *pdev)
continue;
}
rpmpds[i]->rpm = rpm;
rpmpds[i]->max_state = desc->max_state;
rpmpds[i]->pd.power_off = rpmpd_power_off;
rpmpds[i]->pd.power_on = rpmpd_power_on;
......
......@@ -71,6 +71,10 @@ config SYSC_R8A779G0
bool "System Controller support for R-Car V4H" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A779H0
bool "System Controller support for R-Car V4M" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_RMOBILE
bool "System Controller support for R-Mobile" if COMPILE_TEST
......
......@@ -24,6 +24,7 @@ obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o
obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o
obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o
obj-$(CONFIG_SYSC_R8A779G0) += r8a779g0-sysc.o
obj-$(CONFIG_SYSC_R8A779H0) += r8a779h0-sysc.o
# Family
obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o
obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o
......
......@@ -5,19 +5,7 @@
* Copyright (C) 2020 Renesas Electronics Corp.
*/
#include <linux/bits.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <dt-bindings/power/r8a779a0-sysc.h>
......
......@@ -5,19 +5,7 @@
* Copyright (C) 2021 Renesas Electronics Corp.
*/
#include <linux/bits.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <dt-bindings/power/r8a779f0-sysc.h>
......
......@@ -5,19 +5,7 @@
* Copyright (C) 2022 Renesas Electronics Corp.
*/
#include <linux/bits.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <dt-bindings/power/r8a779g0-sysc.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car V4M System Controller
*
* Copyright (C) 2023 Renesas Electronics Corp
*/
#include <linux/kernel.h>
#include <dt-bindings/power/renesas,r8a779h0-sysc.h>
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779h0_areas[] __initdata = {
{ "always-on", R8A779H0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "c4", R8A779H0_PD_C4, R8A779H0_PD_ALWAYS_ON },
{ "a2e0d0", R8A779H0_PD_A2E0D0, R8A779H0_PD_C4, PD_SCU },
{ "a1e0d0c0", R8A779H0_PD_A1E0D0C0, R8A779H0_PD_A2E0D0, PD_CPU_NOCR },
{ "a1e0d0c1", R8A779H0_PD_A1E0D0C1, R8A779H0_PD_A2E0D0, PD_CPU_NOCR },
{ "a1e0d0c2", R8A779H0_PD_A1E0D0C2, R8A779H0_PD_A2E0D0, PD_CPU_NOCR },
{ "a1e0d0c3", R8A779H0_PD_A1E0D0C3, R8A779H0_PD_A2E0D0, PD_CPU_NOCR },
{ "a3cr0", R8A779H0_PD_A3CR0, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR },
{ "a3cr1", R8A779H0_PD_A3CR1, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR },
{ "a3cr2", R8A779H0_PD_A3CR2, R8A779H0_PD_ALWAYS_ON, PD_CPU_NOCR },
{ "a33dga", R8A779H0_PD_A33DGA, R8A779H0_PD_C4 },
{ "a23dgb", R8A779H0_PD_A23DGB, R8A779H0_PD_A33DGA },
{ "a3vip0", R8A779H0_PD_A3VIP0, R8A779H0_PD_C4 },
{ "a3vip2", R8A779H0_PD_A3VIP2, R8A779H0_PD_C4 },
{ "a3dul", R8A779H0_PD_A3DUL, R8A779H0_PD_C4 },
{ "a3isp0", R8A779H0_PD_A3ISP0, R8A779H0_PD_C4 },
{ "a2cn0", R8A779H0_PD_A2CN0, R8A779H0_PD_C4 },
{ "a1cn0", R8A779H0_PD_A1CN0, R8A779H0_PD_A2CN0 },
{ "a1dsp0", R8A779H0_PD_A1DSP0, R8A779H0_PD_A2CN0 },
{ "a1dsp1", R8A779H0_PD_A1DSP1, R8A779H0_PD_A2CN0 },
{ "a2imp01", R8A779H0_PD_A2IMP01, R8A779H0_PD_C4 },
{ "a2psc", R8A779H0_PD_A2PSC, R8A779H0_PD_C4 },
{ "a2dma", R8A779H0_PD_A2DMA, R8A779H0_PD_C4 },
{ "a2cv0", R8A779H0_PD_A2CV0, R8A779H0_PD_C4 },
{ "a2cv1", R8A779H0_PD_A2CV1, R8A779H0_PD_C4 },
{ "a2cv2", R8A779H0_PD_A2CV2, R8A779H0_PD_C4 },
{ "a2cv3", R8A779H0_PD_A2CV3, R8A779H0_PD_C4 },
{ "a3imr0", R8A779H0_PD_A3IMR0, R8A779H0_PD_C4 },
{ "a3imr1", R8A779H0_PD_A3IMR1, R8A779H0_PD_C4 },
{ "a3imr2", R8A779H0_PD_A3IMR2, R8A779H0_PD_C4 },
{ "a3imr3", R8A779H0_PD_A3IMR3, R8A779H0_PD_C4 },
{ "a3vc", R8A779H0_PD_A3VC, R8A779H0_PD_C4 },
{ "a3pci", R8A779H0_PD_A3PCI, R8A779H0_PD_C4 },
{ "a2pciphy", R8A779H0_PD_A2PCIPHY, R8A779H0_PD_A3PCI },
};
const struct rcar_gen4_sysc_info r8a779h0_sysc_info __initconst = {
.areas = r8a779h0_areas,
.num_areas = ARRAY_SIZE(r8a779h0_areas),
};
......@@ -50,13 +50,13 @@
#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */
#define SYSCSR_TIMEOUT 10000
#define SYSCSR_DELAY_US 10
#define SYSCSR_DELAY_US 1
#define PDRESR_RETRIES 1000
#define PDRESR_DELAY_US 10
#define PDRESR_RETRIES 10000
#define PDRESR_DELAY_US 1
#define SYSCISR_TIMEOUT 10000
#define SYSCISR_DELAY_US 10
#define SYSCISCR_TIMEOUT 10000
#define SYSCISCR_DELAY_US 1
#define RCAR_GEN4_PD_ALWAYS_ON 64
#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32)
......@@ -97,7 +97,7 @@ static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask)
ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx),
val, !(val & isr_mask),
SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
SYSCISCR_DELAY_US, SYSCISCR_TIMEOUT);
if (ret < 0) {
pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__);
return -EIO;
......@@ -157,7 +157,7 @@ static int rcar_gen4_sysc_power(u8 pdr, bool on)
/* Wait until the power shutoff or resume request has completed * */
ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx),
val, (val & isr_mask),
SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
SYSCISCR_DELAY_US, SYSCISCR_TIMEOUT);
if (ret < 0) {
ret = -EIO;
goto out;
......@@ -284,6 +284,9 @@ static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = {
#endif
#ifdef CONFIG_SYSC_R8A779G0
{ .compatible = "renesas,r8a779g0-sysc", .data = &r8a779g0_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A779H0
{ .compatible = "renesas,r8a779h0-sysc", .data = &r8a779h0_sysc_info },
#endif
{ /* sentinel */ }
};
......
......@@ -40,5 +40,6 @@ struct rcar_gen4_sysc_info {
extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info;
extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info;
extern const struct rcar_gen4_sysc_info r8a779g0_sysc_info;
extern const struct rcar_gen4_sysc_info r8a779h0_sysc_info;
#endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */
......@@ -45,10 +45,10 @@
#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */
#define SYSCSR_TIMEOUT 100
#define SYSCSR_TIMEOUT 1000
#define SYSCSR_DELAY_US 1
#define PWRER_RETRIES 100
#define PWRER_RETRIES 1000
#define PWRER_DELAY_US 1
#define SYSCISR_TIMEOUT 1000
......
......@@ -305,7 +305,7 @@ static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
}
static struct generic_pm_domain *
tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
tegra_powergate_xlate(const struct of_phandle_args *spec, void *data)
{
struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
struct genpd_onecell_data *genpd = data;
......
......@@ -695,6 +695,8 @@ static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm)
data = prm->data;
name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s",
data->name);
if (!name)
return -ENOMEM;
prmd->dev = dev;
prmd->prm = prm;
......
......@@ -85,7 +85,7 @@ static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
* @data: genpd core data for all the powerdomains on the device
*/
static struct generic_pm_domain *ti_sci_pd_xlate(
struct of_phandle_args *genpdspec,
const struct of_phandle_args *genpdspec,
void *data)
{
struct genpd_onecell_data *genpd_data = data;
......
......@@ -210,7 +210,7 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
}
static struct generic_pm_domain *zynqmp_gpd_xlate
(struct of_phandle_args *genpdspec, void *data)
(const struct of_phandle_args *genpdspec, void *data)
{
struct genpd_onecell_data *genpd_data = data;
unsigned int i, idx = genpdspec->args[0];
......
......@@ -103,12 +103,10 @@ enum imx_dsp_rp_mbox_messages {
* @tx_ch: mailbox tx channel handle
* @rx_ch: mailbox rx channel handle
* @rxdb_ch: mailbox rx doorbell channel handle
* @pd_dev: power domain device
* @pd_dev_link: power domain device link
* @pd_list: power domain list
* @ipc_handle: System Control Unit ipc handle
* @rproc_work: work for processing virtio interrupts
* @pm_comp: completion primitive to sync for suspend response
* @num_domains: power domain number
* @flags: control flags
*/
struct imx_dsp_rproc {
......@@ -121,12 +119,10 @@ struct imx_dsp_rproc {
struct mbox_chan *tx_ch;
struct mbox_chan *rx_ch;
struct mbox_chan *rxdb_ch;
struct device **pd_dev;
struct device_link **pd_dev_link;
struct dev_pm_domain_list *pd_list;
struct imx_sc_ipc *ipc_handle;
struct work_struct rproc_work;
struct completion pm_comp;
int num_domains;
u32 flags;
};
......@@ -955,74 +951,14 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
static int imx_dsp_attach_pm_domains(struct imx_dsp_rproc *priv)
{
struct device *dev = priv->rproc->dev.parent;
int ret, i;
priv->num_domains = of_count_phandle_with_args(dev->of_node,
"power-domains",
"#power-domain-cells");
/* If only one domain, then no need to link the device */
if (priv->num_domains <= 1)
return 0;
priv->pd_dev = devm_kmalloc_array(dev, priv->num_domains,
sizeof(*priv->pd_dev),
GFP_KERNEL);
if (!priv->pd_dev)
return -ENOMEM;
priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_domains,
sizeof(*priv->pd_dev_link),
GFP_KERNEL);
if (!priv->pd_dev_link)
return -ENOMEM;
for (i = 0; i < priv->num_domains; i++) {
priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
if (IS_ERR(priv->pd_dev[i])) {
ret = PTR_ERR(priv->pd_dev[i]);
goto detach_pm;
}
/*
* device_link_add will check priv->pd_dev[i], if it is
* NULL, then will break.
*/
priv->pd_dev_link[i] = device_link_add(dev,
priv->pd_dev[i],
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME);
if (!priv->pd_dev_link[i]) {
dev_pm_domain_detach(priv->pd_dev[i], false);
ret = -EINVAL;
goto detach_pm;
}
}
return 0;
detach_pm:
while (--i >= 0) {
device_link_del(priv->pd_dev_link[i]);
dev_pm_domain_detach(priv->pd_dev[i], false);
}
return ret;
}
static int imx_dsp_detach_pm_domains(struct imx_dsp_rproc *priv)
{
int i;
int ret;
if (priv->num_domains <= 1)
/* A single PM domain is already attached. */
if (dev->pm_domain)
return 0;
for (i = 0; i < priv->num_domains; i++) {
device_link_del(priv->pd_dev_link[i]);
dev_pm_domain_detach(priv->pd_dev[i], false);
}
return 0;
ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
return ret < 0 ? ret : 0;
}
/**
......@@ -1154,7 +1090,7 @@ static int imx_dsp_rproc_probe(struct platform_device *pdev)
return 0;
err_detach_domains:
imx_dsp_detach_pm_domains(priv);
dev_pm_domain_detach_list(priv->pd_list);
err_put_rproc:
rproc_free(rproc);
......@@ -1168,7 +1104,7 @@ static void imx_dsp_rproc_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
rproc_del(rproc);
imx_dsp_detach_pm_domains(priv);
dev_pm_domain_detach_list(priv->pd_list);
rproc_free(rproc);
}
......
......@@ -92,7 +92,6 @@ struct imx_rproc_mem {
static int imx_rproc_xtr_mbox_init(struct rproc *rproc);
static void imx_rproc_free_mbox(struct rproc *rproc);
static int imx_rproc_detach_pd(struct rproc *rproc);
struct imx_rproc {
struct device *dev;
......@@ -113,10 +112,8 @@ struct imx_rproc {
u32 rproc_pt; /* partition id */
u32 rsrc_id; /* resource id */
u32 entry; /* cpu start address */
int num_pd;
u32 core_index;
struct device **pd_dev;
struct device_link **pd_dev_link;
struct dev_pm_domain_list *pd_list;
};
static const struct imx_rproc_att imx_rproc_att_imx93[] = {
......@@ -853,7 +850,7 @@ static void imx_rproc_put_scu(struct rproc *rproc)
return;
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
imx_rproc_detach_pd(rproc);
dev_pm_domain_detach_list(priv->pd_list);
return;
}
......@@ -880,72 +877,20 @@ static int imx_rproc_partition_notify(struct notifier_block *nb,
static int imx_rproc_attach_pd(struct imx_rproc *priv)
{
struct device *dev = priv->dev;
int ret, i;
/*
* If there is only one power-domain entry, the platform driver framework
* will handle it, no need handle it in this driver.
*/
priv->num_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells");
if (priv->num_pd <= 1)
return 0;
priv->pd_dev = devm_kmalloc_array(dev, priv->num_pd, sizeof(*priv->pd_dev), GFP_KERNEL);
if (!priv->pd_dev)
return -ENOMEM;
priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_pd, sizeof(*priv->pd_dev_link),
GFP_KERNEL);
if (!priv->pd_dev_link)
return -ENOMEM;
for (i = 0; i < priv->num_pd; i++) {
priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
if (IS_ERR(priv->pd_dev[i])) {
ret = PTR_ERR(priv->pd_dev[i]);
goto detach_pd;
}
priv->pd_dev_link[i] = device_link_add(dev, priv->pd_dev[i], DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
if (!priv->pd_dev_link[i]) {
dev_pm_domain_detach(priv->pd_dev[i], false);
ret = -EINVAL;
goto detach_pd;
}
}
return 0;
detach_pd:
while (--i >= 0) {
device_link_del(priv->pd_dev_link[i]);
dev_pm_domain_detach(priv->pd_dev[i], false);
}
return ret;
}
static int imx_rproc_detach_pd(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
int i;
int ret;
struct dev_pm_domain_attach_data pd_data = {
.pd_flags = PD_FLAG_DEV_LINK_ON,
};
/*
* If there is only one power-domain entry, the platform driver framework
* will handle it, no need handle it in this driver.
*/
if (priv->num_pd <= 1)
if (dev->pm_domain)
return 0;
for (i = 0; i < priv->num_pd; i++) {
device_link_del(priv->pd_dev_link[i]);
dev_pm_domain_detach(priv->pd_dev[i], false);
}
return 0;
ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list);
return ret < 0 ? ret : 0;
}
static int imx_rproc_detect_mode(struct imx_rproc *priv)
......
......@@ -55,8 +55,6 @@
#define QDSP6SS_CORE_CBCR 0x20
#define QDSP6SS_SLEEP_CBCR 0x3c
#define QCOM_Q6V5_RPROC_PROXY_PD_MAX 3
#define LPASS_BOOT_CORE_START BIT(0)
#define LPASS_BOOT_CMD_START BIT(0)
#define LPASS_EFUSE_Q6SS_EVB_SEL 0x0
......@@ -74,7 +72,8 @@ struct adsp_pil_data {
const char **clk_ids;
int num_clks;
const char **proxy_pd_names;
const char **pd_names;
unsigned int num_pds;
const char *load_state;
};
......@@ -110,8 +109,7 @@ struct qcom_adsp {
size_t mem_size;
bool has_iommu;
struct device *proxy_pds[QCOM_Q6V5_RPROC_PROXY_PD_MAX];
size_t proxy_pd_count;
struct dev_pm_domain_list *pd_list;
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_ssr ssr_subdev;
......@@ -120,98 +118,92 @@ struct qcom_adsp {
int (*shutdown)(struct qcom_adsp *adsp);
};
static int qcom_rproc_pds_attach(struct device *dev, struct qcom_adsp *adsp,
const char **pd_names)
static int qcom_rproc_pds_attach(struct qcom_adsp *adsp, const char **pd_names,
unsigned int num_pds)
{
struct device **devs = adsp->proxy_pds;
size_t num_pds = 0;
struct device *dev = adsp->dev;
struct dev_pm_domain_attach_data pd_data = {
.pd_names = pd_names,
.num_pd_names = num_pds,
};
int ret;
int i;
if (!pd_names)
return 0;
/* Handle single power domain */
if (dev->pm_domain) {
devs[0] = dev;
pm_runtime_enable(dev);
return 1;
}
if (dev->pm_domain)
goto out;
while (pd_names[num_pds])
num_pds++;
if (!pd_names)
return 0;
if (num_pds > ARRAY_SIZE(adsp->proxy_pds))
return -E2BIG;
ret = dev_pm_domain_attach_list(dev, &pd_data, &adsp->pd_list);
if (ret < 0)
return ret;
for (i = 0; i < num_pds; i++) {
devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
if (IS_ERR_OR_NULL(devs[i])) {
ret = PTR_ERR(devs[i]) ? : -ENODATA;
goto unroll_attach;
}
}
out:
pm_runtime_enable(dev);
return 0;
}
return num_pds;
static void qcom_rproc_pds_detach(struct qcom_adsp *adsp)
{
struct device *dev = adsp->dev;
struct dev_pm_domain_list *pds = adsp->pd_list;
unroll_attach:
for (i--; i >= 0; i--)
dev_pm_domain_detach(devs[i], false);
dev_pm_domain_detach_list(pds);
return ret;
if (dev->pm_domain || pds)
pm_runtime_disable(adsp->dev);
}
static void qcom_rproc_pds_detach(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
static int qcom_rproc_pds_enable(struct qcom_adsp *adsp)
{
struct device *dev = adsp->dev;
int i;
struct dev_pm_domain_list *pds = adsp->pd_list;
int ret, i = 0;
/* Handle single power domain */
if (dev->pm_domain && pd_count) {
pm_runtime_disable(dev);
return;
}
if (!dev->pm_domain && !pds)
return 0;
for (i = 0; i < pd_count; i++)
dev_pm_domain_detach(pds[i], false);
}
if (dev->pm_domain)
dev_pm_genpd_set_performance_state(dev, INT_MAX);
static int qcom_rproc_pds_enable(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
{
int ret;
int i;
for (i = 0; i < pd_count; i++) {
dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
ret = pm_runtime_resume_and_get(pds[i]);
if (ret < 0) {
dev_pm_genpd_set_performance_state(pds[i], 0);
goto unroll_pd_votes;
}
while (pds && i < pds->num_pds) {
dev_pm_genpd_set_performance_state(pds->pd_devs[i], INT_MAX);
i++;
}
return 0;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
while (pds && i > 0) {
i--;
dev_pm_genpd_set_performance_state(pds->pd_devs[i], 0);
}
unroll_pd_votes:
for (i--; i >= 0; i--) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
if (dev->pm_domain)
dev_pm_genpd_set_performance_state(dev, 0);
}
return ret;
}
static void qcom_rproc_pds_disable(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
static void qcom_rproc_pds_disable(struct qcom_adsp *adsp)
{
int i;
struct device *dev = adsp->dev;
struct dev_pm_domain_list *pds = adsp->pd_list;
int i = 0;
if (!dev->pm_domain && !pds)
return;
if (dev->pm_domain)
dev_pm_genpd_set_performance_state(dev, 0);
for (i = 0; i < pd_count; i++) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
while (pds && i < pds->num_pds) {
dev_pm_genpd_set_performance_state(pds->pd_devs[i], 0);
i++;
}
pm_runtime_put(dev);
}
static int qcom_wpss_shutdown(struct qcom_adsp *adsp)
......@@ -397,8 +389,7 @@ static int adsp_start(struct rproc *rproc)
if (ret)
goto adsp_smmu_unmap;
ret = qcom_rproc_pds_enable(adsp, adsp->proxy_pds,
adsp->proxy_pd_count);
ret = qcom_rproc_pds_enable(adsp);
if (ret < 0)
goto disable_xo_clk;
......@@ -448,7 +439,7 @@ static int adsp_start(struct rproc *rproc)
disable_adsp_clks:
clk_bulk_disable_unprepare(adsp->num_clks, adsp->clks);
disable_power_domain:
qcom_rproc_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
qcom_rproc_pds_disable(adsp);
disable_xo_clk:
clk_disable_unprepare(adsp->xo);
adsp_smmu_unmap:
......@@ -464,7 +455,7 @@ static void qcom_adsp_pil_handover(struct qcom_q6v5 *q6v5)
struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5);
clk_disable_unprepare(adsp->xo);
qcom_rproc_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
qcom_rproc_pds_disable(adsp);
}
static int adsp_stop(struct rproc *rproc)
......@@ -715,13 +706,11 @@ static int adsp_probe(struct platform_device *pdev)
if (ret)
goto free_rproc;
ret = qcom_rproc_pds_attach(adsp->dev, adsp,
desc->proxy_pd_names);
ret = qcom_rproc_pds_attach(adsp, desc->pd_names, desc->num_pds);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to attach proxy power domains\n");
goto free_rproc;
}
adsp->proxy_pd_count = ret;
ret = adsp_init_reset(adsp);
if (ret)
......@@ -753,7 +742,7 @@ static int adsp_probe(struct platform_device *pdev)
return 0;
disable_pm:
qcom_rproc_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
qcom_rproc_pds_detach(adsp);
free_rproc:
rproc_free(rproc);
......@@ -771,7 +760,7 @@ static void adsp_remove(struct platform_device *pdev)
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
qcom_remove_sysmon_subdev(adsp->sysmon);
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
qcom_rproc_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
qcom_rproc_pds_detach(adsp);
rproc_free(adsp->rproc);
}
......@@ -788,9 +777,8 @@ static const struct adsp_pil_data adsp_resource_init = {
"qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", NULL
},
.num_clks = 7,
.proxy_pd_names = (const char*[]) {
"cx", NULL
},
.pd_names = (const char*[]) { "cx" },
.num_pds = 1,
};
static const struct adsp_pil_data adsp_sc7280_resource_init = {
......@@ -821,9 +809,8 @@ static const struct adsp_pil_data cdsp_resource_init = {
"q6_axim", NULL
},
.num_clks = 7,
.proxy_pd_names = (const char*[]) {
"cx", NULL
},
.pd_names = (const char*[]) { "cx" },
.num_pds = 1,
};
static const struct adsp_pil_data wpss_resource_init = {
......@@ -839,9 +826,8 @@ static const struct adsp_pil_data wpss_resource_init = {
"ahb_bdg", "ahb", "rscp", NULL
},
.num_clks = 3,
.proxy_pd_names = (const char*[]) {
"cx", "mx", NULL
},
.pd_names = (const char*[]) { "cx", "mx" },
.num_pds = 2,
};
static const struct of_device_id adsp_of_match[] = {
......
......@@ -308,6 +308,13 @@
#define MSM8953_VDDMX 5
#define MSM8953_VDDMX_AO 6
/* MSM8974 Power Domain Indexes */
#define MSM8974_VDDCX 0
#define MSM8974_VDDCX_AO 1
#define MSM8974_VDDCX_VFC 2
#define MSM8974_VDDGFX 3
#define MSM8974_VDDGFX_VFC 4
/* MSM8976 Power Domain Indexes */
#define MSM8976_VDDCX 0
#define MSM8976_VDDCX_AO 1
......
......@@ -19,6 +19,33 @@
#include <linux/cpumask.h>
#include <linux/time64.h>
/*
* Flags to control the behaviour when attaching a device to its PM domains.
*
* PD_FLAG_NO_DEV_LINK: As the default behaviour creates a device-link
* for every PM domain that gets attached, this
* flag can be used to skip that.
*
* PD_FLAG_DEV_LINK_ON: Add the DL_FLAG_RPM_ACTIVE to power-on the
* supplier and its PM domain when creating the
* device-links.
*
*/
#define PD_FLAG_NO_DEV_LINK BIT(0)
#define PD_FLAG_DEV_LINK_ON BIT(1)
struct dev_pm_domain_attach_data {
const char * const *pd_names;
const u32 num_pd_names;
const u32 pd_flags;
};
struct dev_pm_domain_list {
struct device **pd_devs;
struct device_link **pd_links;
u32 num_pds;
};
/*
* Flags to control the behaviour of a genpd.
*
......@@ -322,7 +349,7 @@ static inline void dev_pm_genpd_resume(struct device *dev) {}
/* OF PM domain providers */
struct of_device_id;
typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
typedef struct generic_pm_domain *(*genpd_xlate_t)(const struct of_phandle_args *args,
void *data);
struct genpd_onecell_data {
......@@ -337,11 +364,11 @@ int of_genpd_add_provider_simple(struct device_node *np,
int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data);
void of_genpd_del_provider(struct device_node *np);
int of_genpd_add_device(struct of_phandle_args *args, struct device *dev);
int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec);
int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec);
int of_genpd_add_device(const struct of_phandle_args *args, struct device *dev);
int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec);
int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec);
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n);
......@@ -366,20 +393,20 @@ static inline int of_genpd_add_provider_onecell(struct device_node *np,
static inline void of_genpd_del_provider(struct device_node *np) {}
static inline int of_genpd_add_device(struct of_phandle_args *args,
static inline int of_genpd_add_device(const struct of_phandle_args *args,
struct device *dev)
{
return -ENODEV;
}
static inline int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
static inline int of_genpd_add_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec)
{
return -ENODEV;
}
static inline int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
static inline int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec,
const struct of_phandle_args *subdomain_spec)
{
return -ENODEV;
}
......@@ -420,7 +447,11 @@ struct device *dev_pm_domain_attach_by_id(struct device *dev,
unsigned int index);
struct device *dev_pm_domain_attach_by_name(struct device *dev,
const char *name);
int dev_pm_domain_attach_list(struct device *dev,
const struct dev_pm_domain_attach_data *data,
struct dev_pm_domain_list **list);
void dev_pm_domain_detach(struct device *dev, bool power_off);
void dev_pm_domain_detach_list(struct dev_pm_domain_list *list);
int dev_pm_domain_start(struct device *dev);
void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state);
......@@ -439,7 +470,14 @@ static inline struct device *dev_pm_domain_attach_by_name(struct device *dev,
{
return NULL;
}
static inline int dev_pm_domain_attach_list(struct device *dev,
const struct dev_pm_domain_attach_data *data,
struct dev_pm_domain_list **list)
{
return 0;
}
static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {}
static inline void dev_pm_domain_detach_list(struct dev_pm_domain_list *list) {}
static inline int dev_pm_domain_start(struct device *dev)
{
return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment