blob: 055db01b886356a3e15ca91578d3c364329d1b8d [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/amlogic/pwr_ctrl.h>
#include <dt-bindings/power/cx-pd.h>
struct cx_pm_domain {
struct generic_pm_domain base;
int pd_index;
bool pd_status;
};
static inline struct cx_pm_domain *
to_cx_pm_domain(struct generic_pm_domain *genpd)
{
return container_of(genpd, struct cx_pm_domain, base);
}
static int cx_pm_domain_power_off(struct generic_pm_domain *genpd)
{
struct cx_pm_domain *pd = to_cx_pm_domain(genpd);
pwr_ctrl_psci_smc(pd->pd_index, PWR_OFF);
return 0;
}
static int cx_pm_domain_power_on(struct generic_pm_domain *genpd)
{
struct cx_pm_domain *pd = to_cx_pm_domain(genpd);
pwr_ctrl_psci_smc(pd->pd_index, PWR_ON);
return 0;
}
#define POWER_DOMAIN(_name, index, status, flag) \
struct cx_pm_domain _name = { \
.base = { \
.name = #_name, \
.power_off = cx_pm_domain_power_off, \
.power_on = cx_pm_domain_power_on, \
.flags = flag, \
}, \
.pd_index = index, \
.pd_status = status, \
}
static POWER_DOMAIN(cpu_pwr0, PDID_CPU_PWR0, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(cpu_core0, PDID_CPU_CORE0, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(cpu_core1, PDID_CPU_CORE1, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(sptop, PDID_SPTOP, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(dsp_a, PDID_DSP_A, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(dsp_b, PDID_DSP_B, DOMAIN_INIT_ON,
GENPD_FLAG_ACTIVE_WAKEUP);
static POWER_DOMAIN(uart, PDID_UART, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(dmc, PDID_DMC, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
/* If there is GENPD_FLAG_ALWAYS_ON, the domian must be initialized to on */
static POWER_DOMAIN(i2c, PDID_I2C, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(sdemmc_b, PDID_SDEMMC_B, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(acodec, PDID_ACODEC, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(audio, PDID_AUDIO, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(mkl_otp, PDID_MKL_OTP, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(dma, PDID_DMA, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE);
static POWER_DOMAIN(sdemmc_a, PDID_SDEMMC_A, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(sram_a, PDID_SRAM_A, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(sram_b, PDID_SRAM_B, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(ir, PDID_IR, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(spicc, PDID_SPICC, DOMAIN_INIT_OFF, 0);
static POWER_DOMAIN(spifc, PDID_SPIFC, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(usb, PDID_USB, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(nic, PDID_NIC, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(pdm, PDID_PDM, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(rsa, PDID_RSA, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(mipi_isp, PDID_MIPI_ISP, DOMAIN_INIT_ON,
GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(hcodec, PDID_HCODEC, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(wave, PDID_WAVE, DOMAIN_INIT_OFF, 0);
static POWER_DOMAIN(sdemmc_c, PDID_SDEMMC_C, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(sram_c, PDID_SRAM_C, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(gdc, PDID_GDC, DOMAIN_INIT_OFF, 0);
static POWER_DOMAIN(ge2d, PDID_GE2D, DOMAIN_INIT_OFF, 0);
static POWER_DOMAIN(nna, PDID_NNA, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(eth, PDID_ETH, DOMAIN_INIT_ON, 0);
static POWER_DOMAIN(gic, PDID_GIC, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(ddr, PDID_DDR, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static POWER_DOMAIN(spicc_b, PDID_SPICC_B, DOMAIN_INIT_ON, GENPD_FLAG_ALWAYS_ON);
static struct generic_pm_domain *cx_onecell_domains[] = {
[PDID_CPU_PWR0] = &cpu_pwr0.base,
[PDID_CPU_CORE0] = &cpu_core0.base,
[PDID_CPU_CORE1] = &cpu_core1.base,
[3] = NULL,
[4] = NULL,
[PDID_SPTOP] = &sptop.base,
[6] = NULL,
[7] = NULL,
[PDID_DSP_A] = &dsp_a.base,
[PDID_DSP_B] = &dsp_b.base,
[PDID_UART] = &uart.base,
[PDID_DMC] = &dmc.base,
[PDID_I2C] = &i2c.base,
[PDID_SDEMMC_B] = &sdemmc_b.base,
[PDID_ACODEC] = &acodec.base,
[PDID_AUDIO] = &audio.base,
[PDID_MKL_OTP] = &mkl_otp.base,
[PDID_DMA] = &dma.base,
[PDID_SDEMMC_A] = &sdemmc_a.base,
[PDID_SRAM_A] = &sram_a.base,
[PDID_SRAM_B] = &sram_b.base,
[PDID_IR] = &ir.base,
[PDID_SPICC] = &spicc.base,
[PDID_SPIFC] = &spifc.base,
[PDID_USB] = &usb.base,
[PDID_NIC] = &nic.base,
[PDID_PDM] = &pdm.base,
[PDID_RSA] = &rsa.base,
[PDID_MIPI_ISP] = &mipi_isp.base,
[PDID_HCODEC] = &hcodec.base,
[PDID_WAVE] = &wave.base,
[PDID_SDEMMC_C] = &sdemmc_c.base,
[PDID_SRAM_C] = &sram_c.base,
[PDID_GDC] = &gdc.base,
[PDID_GE2D] = &ge2d.base,
[PDID_NNA] = &nna.base,
[PDID_ETH] = &eth.base,
[PDID_GIC] = &gic.base,
[PDID_DDR] = &ddr.base,
[PDID_SPICC_B] = &spicc_b.base,
};
static struct genpd_onecell_data cx_pd_onecell_data = {
.domains = cx_onecell_domains,
.num_domains = 40,
};
static int cx_pd_probe(struct platform_device *pdev)
{
int ret, i;
bool init_status;
struct cx_pm_domain *pd;
for (i = 0; i < cx_pd_onecell_data.num_domains; i++) {
/* array might be sparse */
if (!cx_pd_onecell_data.domains[i])
continue;
/* Initialize based on pd_status */
pd = to_cx_pm_domain(cx_pd_onecell_data.domains[i]);
if (pwr_ctrl_status_psci_smc(pd->pd_index) == -1 ||
pd->base.flags == GENPD_FLAG_ALWAYS_ON)
init_status = pd->pd_status;
else
init_status = pwr_ctrl_status_psci_smc(pd->pd_index);
if (pd->base.flags & GENPD_FLAG_ALWAYS_ON)
pm_genpd_init(cx_pd_onecell_data.domains[i],
&pm_domain_always_on_gov, init_status);
else
pm_genpd_init(cx_pd_onecell_data.domains[i],
NULL, init_status);
}
pd_dev_create_file(&pdev->dev, PDID_DSP_A, PDID_MAX,
cx_onecell_domains);
ret = of_genpd_add_provider_onecell(pdev->dev.of_node,
&cx_pd_onecell_data);
if (ret)
goto out;
return 0;
out:
pd_dev_remove_file(&pdev->dev);
return ret;
}
static const struct of_device_id pd_match_table[] = {
{ .compatible = "amlogic,c1-power-domain" },
{ .compatible = "amlogic,c2-power-domain" },
{}
};
static struct platform_driver cx_pd_driver = {
.probe = cx_pd_probe,
.driver = {
.name = "cx_pd",
.of_match_table = pd_match_table,
},
};
static int cx_pd_init(void)
{
return platform_driver_register(&cx_pd_driver);
}
arch_initcall_sync(cx_pd_init);