162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Actions Semi Owl Smart Power System (SPS) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2012 Actions Semi Inc. 662306a36Sopenharmony_ci * Author: Actions Semi, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2017 Andreas Färber 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_platform.h> 1362306a36Sopenharmony_ci#include <linux/pm_domain.h> 1462306a36Sopenharmony_ci#include <linux/soc/actions/owl-sps.h> 1562306a36Sopenharmony_ci#include <dt-bindings/power/owl-s500-powergate.h> 1662306a36Sopenharmony_ci#include <dt-bindings/power/owl-s700-powergate.h> 1762306a36Sopenharmony_ci#include <dt-bindings/power/owl-s900-powergate.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct owl_sps_domain_info { 2062306a36Sopenharmony_ci const char *name; 2162306a36Sopenharmony_ci int pwr_bit; 2262306a36Sopenharmony_ci int ack_bit; 2362306a36Sopenharmony_ci unsigned int genpd_flags; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct owl_sps_info { 2762306a36Sopenharmony_ci unsigned num_domains; 2862306a36Sopenharmony_ci const struct owl_sps_domain_info *domains; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct owl_sps { 3262306a36Sopenharmony_ci struct device *dev; 3362306a36Sopenharmony_ci const struct owl_sps_info *info; 3462306a36Sopenharmony_ci void __iomem *base; 3562306a36Sopenharmony_ci struct genpd_onecell_data genpd_data; 3662306a36Sopenharmony_ci struct generic_pm_domain *domains[]; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct owl_sps_domain { 4262306a36Sopenharmony_ci struct generic_pm_domain genpd; 4362306a36Sopenharmony_ci const struct owl_sps_domain_info *info; 4462306a36Sopenharmony_ci struct owl_sps *sps; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci u32 pwr_mask, ack_mask; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ack_mask = BIT(pd->info->ack_bit); 5262306a36Sopenharmony_ci pwr_mask = BIT(pd->info->pwr_bit); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int owl_sps_power_on(struct generic_pm_domain *domain) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct owl_sps_domain *pd = to_owl_pd(domain); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci dev_dbg(pd->sps->dev, "%s power on", pd->info->name); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return owl_sps_set_power(pd, true); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int owl_sps_power_off(struct generic_pm_domain *domain) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct owl_sps_domain *pd = to_owl_pd(domain); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci dev_dbg(pd->sps->dev, "%s power off", pd->info->name); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return owl_sps_set_power(pd, false); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int owl_sps_init_domain(struct owl_sps *sps, int index) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct owl_sps_domain *pd; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); 8062306a36Sopenharmony_ci if (!pd) 8162306a36Sopenharmony_ci return -ENOMEM; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pd->info = &sps->info->domains[index]; 8462306a36Sopenharmony_ci pd->sps = sps; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci pd->genpd.name = pd->info->name; 8762306a36Sopenharmony_ci pd->genpd.power_on = owl_sps_power_on; 8862306a36Sopenharmony_ci pd->genpd.power_off = owl_sps_power_off; 8962306a36Sopenharmony_ci pd->genpd.flags = pd->info->genpd_flags; 9062306a36Sopenharmony_ci pm_genpd_init(&pd->genpd, NULL, false); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci sps->genpd_data.domains[index] = &pd->genpd; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int owl_sps_probe(struct platform_device *pdev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci const struct of_device_id *match; 10062306a36Sopenharmony_ci const struct owl_sps_info *sps_info; 10162306a36Sopenharmony_ci struct owl_sps *sps; 10262306a36Sopenharmony_ci int i, ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!pdev->dev.of_node) { 10562306a36Sopenharmony_ci dev_err(&pdev->dev, "no device node\n"); 10662306a36Sopenharmony_ci return -ENODEV; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); 11062306a36Sopenharmony_ci if (!match || !match->data) { 11162306a36Sopenharmony_ci dev_err(&pdev->dev, "unknown compatible or missing data\n"); 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci sps_info = match->data; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci sps = devm_kzalloc(&pdev->dev, 11862306a36Sopenharmony_ci struct_size(sps, domains, sps_info->num_domains), 11962306a36Sopenharmony_ci GFP_KERNEL); 12062306a36Sopenharmony_ci if (!sps) 12162306a36Sopenharmony_ci return -ENOMEM; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); 12462306a36Sopenharmony_ci if (IS_ERR(sps->base)) { 12562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to map sps registers\n"); 12662306a36Sopenharmony_ci return PTR_ERR(sps->base); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci sps->dev = &pdev->dev; 13062306a36Sopenharmony_ci sps->info = sps_info; 13162306a36Sopenharmony_ci sps->genpd_data.domains = sps->domains; 13262306a36Sopenharmony_ci sps->genpd_data.num_domains = sps_info->num_domains; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < sps_info->num_domains; i++) { 13562306a36Sopenharmony_ci ret = owl_sps_init_domain(sps, i); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); 14162306a36Sopenharmony_ci if (ret) { 14262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to add provider (%d)", ret); 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const struct owl_sps_domain_info s500_sps_domains[] = { 15062306a36Sopenharmony_ci [S500_PD_VDE] = { 15162306a36Sopenharmony_ci .name = "VDE", 15262306a36Sopenharmony_ci .pwr_bit = 0, 15362306a36Sopenharmony_ci .ack_bit = 16, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci [S500_PD_VCE_SI] = { 15662306a36Sopenharmony_ci .name = "VCE_SI", 15762306a36Sopenharmony_ci .pwr_bit = 1, 15862306a36Sopenharmony_ci .ack_bit = 17, 15962306a36Sopenharmony_ci }, 16062306a36Sopenharmony_ci [S500_PD_USB2_1] = { 16162306a36Sopenharmony_ci .name = "USB2_1", 16262306a36Sopenharmony_ci .pwr_bit = 2, 16362306a36Sopenharmony_ci .ack_bit = 18, 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci [S500_PD_CPU2] = { 16662306a36Sopenharmony_ci .name = "CPU2", 16762306a36Sopenharmony_ci .pwr_bit = 5, 16862306a36Sopenharmony_ci .ack_bit = 21, 16962306a36Sopenharmony_ci .genpd_flags = GENPD_FLAG_ALWAYS_ON, 17062306a36Sopenharmony_ci }, 17162306a36Sopenharmony_ci [S500_PD_CPU3] = { 17262306a36Sopenharmony_ci .name = "CPU3", 17362306a36Sopenharmony_ci .pwr_bit = 6, 17462306a36Sopenharmony_ci .ack_bit = 22, 17562306a36Sopenharmony_ci .genpd_flags = GENPD_FLAG_ALWAYS_ON, 17662306a36Sopenharmony_ci }, 17762306a36Sopenharmony_ci [S500_PD_DMA] = { 17862306a36Sopenharmony_ci .name = "DMA", 17962306a36Sopenharmony_ci .pwr_bit = 8, 18062306a36Sopenharmony_ci .ack_bit = 12, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci [S500_PD_DS] = { 18362306a36Sopenharmony_ci .name = "DS", 18462306a36Sopenharmony_ci .pwr_bit = 9, 18562306a36Sopenharmony_ci .ack_bit = 13, 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci [S500_PD_USB3] = { 18862306a36Sopenharmony_ci .name = "USB3", 18962306a36Sopenharmony_ci .pwr_bit = 10, 19062306a36Sopenharmony_ci .ack_bit = 14, 19162306a36Sopenharmony_ci }, 19262306a36Sopenharmony_ci [S500_PD_USB2_0] = { 19362306a36Sopenharmony_ci .name = "USB2_0", 19462306a36Sopenharmony_ci .pwr_bit = 11, 19562306a36Sopenharmony_ci .ack_bit = 15, 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct owl_sps_info s500_sps_info = { 20062306a36Sopenharmony_ci .num_domains = ARRAY_SIZE(s500_sps_domains), 20162306a36Sopenharmony_ci .domains = s500_sps_domains, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic const struct owl_sps_domain_info s700_sps_domains[] = { 20562306a36Sopenharmony_ci [S700_PD_VDE] = { 20662306a36Sopenharmony_ci .name = "VDE", 20762306a36Sopenharmony_ci .pwr_bit = 0, 20862306a36Sopenharmony_ci }, 20962306a36Sopenharmony_ci [S700_PD_VCE_SI] = { 21062306a36Sopenharmony_ci .name = "VCE_SI", 21162306a36Sopenharmony_ci .pwr_bit = 1, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci [S700_PD_USB2_1] = { 21462306a36Sopenharmony_ci .name = "USB2_1", 21562306a36Sopenharmony_ci .pwr_bit = 2, 21662306a36Sopenharmony_ci }, 21762306a36Sopenharmony_ci [S700_PD_HDE] = { 21862306a36Sopenharmony_ci .name = "HDE", 21962306a36Sopenharmony_ci .pwr_bit = 7, 22062306a36Sopenharmony_ci }, 22162306a36Sopenharmony_ci [S700_PD_DMA] = { 22262306a36Sopenharmony_ci .name = "DMA", 22362306a36Sopenharmony_ci .pwr_bit = 8, 22462306a36Sopenharmony_ci }, 22562306a36Sopenharmony_ci [S700_PD_DS] = { 22662306a36Sopenharmony_ci .name = "DS", 22762306a36Sopenharmony_ci .pwr_bit = 9, 22862306a36Sopenharmony_ci }, 22962306a36Sopenharmony_ci [S700_PD_USB3] = { 23062306a36Sopenharmony_ci .name = "USB3", 23162306a36Sopenharmony_ci .pwr_bit = 10, 23262306a36Sopenharmony_ci }, 23362306a36Sopenharmony_ci [S700_PD_USB2_0] = { 23462306a36Sopenharmony_ci .name = "USB2_0", 23562306a36Sopenharmony_ci .pwr_bit = 11, 23662306a36Sopenharmony_ci }, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct owl_sps_info s700_sps_info = { 24062306a36Sopenharmony_ci .num_domains = ARRAY_SIZE(s700_sps_domains), 24162306a36Sopenharmony_ci .domains = s700_sps_domains, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic const struct owl_sps_domain_info s900_sps_domains[] = { 24562306a36Sopenharmony_ci [S900_PD_GPU_B] = { 24662306a36Sopenharmony_ci .name = "GPU_B", 24762306a36Sopenharmony_ci .pwr_bit = 3, 24862306a36Sopenharmony_ci }, 24962306a36Sopenharmony_ci [S900_PD_VCE] = { 25062306a36Sopenharmony_ci .name = "VCE", 25162306a36Sopenharmony_ci .pwr_bit = 4, 25262306a36Sopenharmony_ci }, 25362306a36Sopenharmony_ci [S900_PD_SENSOR] = { 25462306a36Sopenharmony_ci .name = "SENSOR", 25562306a36Sopenharmony_ci .pwr_bit = 5, 25662306a36Sopenharmony_ci }, 25762306a36Sopenharmony_ci [S900_PD_VDE] = { 25862306a36Sopenharmony_ci .name = "VDE", 25962306a36Sopenharmony_ci .pwr_bit = 6, 26062306a36Sopenharmony_ci }, 26162306a36Sopenharmony_ci [S900_PD_HDE] = { 26262306a36Sopenharmony_ci .name = "HDE", 26362306a36Sopenharmony_ci .pwr_bit = 7, 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci [S900_PD_USB3] = { 26662306a36Sopenharmony_ci .name = "USB3", 26762306a36Sopenharmony_ci .pwr_bit = 8, 26862306a36Sopenharmony_ci }, 26962306a36Sopenharmony_ci [S900_PD_DDR0] = { 27062306a36Sopenharmony_ci .name = "DDR0", 27162306a36Sopenharmony_ci .pwr_bit = 9, 27262306a36Sopenharmony_ci }, 27362306a36Sopenharmony_ci [S900_PD_DDR1] = { 27462306a36Sopenharmony_ci .name = "DDR1", 27562306a36Sopenharmony_ci .pwr_bit = 10, 27662306a36Sopenharmony_ci }, 27762306a36Sopenharmony_ci [S900_PD_DE] = { 27862306a36Sopenharmony_ci .name = "DE", 27962306a36Sopenharmony_ci .pwr_bit = 13, 28062306a36Sopenharmony_ci }, 28162306a36Sopenharmony_ci [S900_PD_NAND] = { 28262306a36Sopenharmony_ci .name = "NAND", 28362306a36Sopenharmony_ci .pwr_bit = 14, 28462306a36Sopenharmony_ci }, 28562306a36Sopenharmony_ci [S900_PD_USB2_H0] = { 28662306a36Sopenharmony_ci .name = "USB2_H0", 28762306a36Sopenharmony_ci .pwr_bit = 15, 28862306a36Sopenharmony_ci }, 28962306a36Sopenharmony_ci [S900_PD_USB2_H1] = { 29062306a36Sopenharmony_ci .name = "USB2_H1", 29162306a36Sopenharmony_ci .pwr_bit = 16, 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct owl_sps_info s900_sps_info = { 29662306a36Sopenharmony_ci .num_domains = ARRAY_SIZE(s900_sps_domains), 29762306a36Sopenharmony_ci .domains = s900_sps_domains, 29862306a36Sopenharmony_ci}; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic const struct of_device_id owl_sps_of_matches[] = { 30162306a36Sopenharmony_ci { .compatible = "actions,s500-sps", .data = &s500_sps_info }, 30262306a36Sopenharmony_ci { .compatible = "actions,s700-sps", .data = &s700_sps_info }, 30362306a36Sopenharmony_ci { .compatible = "actions,s900-sps", .data = &s900_sps_info }, 30462306a36Sopenharmony_ci { } 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic struct platform_driver owl_sps_platform_driver = { 30862306a36Sopenharmony_ci .probe = owl_sps_probe, 30962306a36Sopenharmony_ci .driver = { 31062306a36Sopenharmony_ci .name = "owl-sps", 31162306a36Sopenharmony_ci .of_match_table = owl_sps_of_matches, 31262306a36Sopenharmony_ci .suppress_bind_attrs = true, 31362306a36Sopenharmony_ci }, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int __init owl_sps_init(void) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return platform_driver_register(&owl_sps_platform_driver); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_cipostcore_initcall(owl_sps_init); 321