18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Marvell Dove PMU support 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/io.h> 68c2ecf20Sopenharmony_ci#include <linux/irq.h> 78c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 108c2ecf20Sopenharmony_ci#include <linux/of_address.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 138c2ecf20Sopenharmony_ci#include <linux/reset.h> 148c2ecf20Sopenharmony_ci#include <linux/reset-controller.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/soc/dove/pmu.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define NR_PMU_IRQS 7 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define PMC_SW_RST 0x30 238c2ecf20Sopenharmony_ci#define PMC_IRQ_CAUSE 0x50 248c2ecf20Sopenharmony_ci#define PMC_IRQ_MASK 0x54 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PMU_PWR 0x10 278c2ecf20Sopenharmony_ci#define PMU_ISO 0x58 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct pmu_data { 308c2ecf20Sopenharmony_ci spinlock_t lock; 318c2ecf20Sopenharmony_ci struct device_node *of_node; 328c2ecf20Sopenharmony_ci void __iomem *pmc_base; 338c2ecf20Sopenharmony_ci void __iomem *pmu_base; 348c2ecf20Sopenharmony_ci struct irq_chip_generic *irq_gc; 358c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 368c2ecf20Sopenharmony_ci#ifdef CONFIG_RESET_CONTROLLER 378c2ecf20Sopenharmony_ci struct reset_controller_dev reset; 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * The PMU contains a register to reset various subsystems within the 438c2ecf20Sopenharmony_ci * SoC. Export this as a reset controller. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci#ifdef CONFIG_RESET_CONTROLLER 468c2ecf20Sopenharmony_ci#define rcdev_to_pmu(rcdev) container_of(rcdev, struct pmu_data, reset) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int pmu_reset_reset(struct reset_controller_dev *rc, unsigned long id) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct pmu_data *pmu = rcdev_to_pmu(rc); 518c2ecf20Sopenharmony_ci unsigned long flags; 528c2ecf20Sopenharmony_ci u32 val; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 558c2ecf20Sopenharmony_ci val = readl_relaxed(pmu->pmc_base + PMC_SW_RST); 568c2ecf20Sopenharmony_ci writel_relaxed(val & ~BIT(id), pmu->pmc_base + PMC_SW_RST); 578c2ecf20Sopenharmony_ci writel_relaxed(val | BIT(id), pmu->pmc_base + PMC_SW_RST); 588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int pmu_reset_assert(struct reset_controller_dev *rc, unsigned long id) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct pmu_data *pmu = rcdev_to_pmu(rc); 668c2ecf20Sopenharmony_ci unsigned long flags; 678c2ecf20Sopenharmony_ci u32 val = ~BIT(id); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 708c2ecf20Sopenharmony_ci val &= readl_relaxed(pmu->pmc_base + PMC_SW_RST); 718c2ecf20Sopenharmony_ci writel_relaxed(val, pmu->pmc_base + PMC_SW_RST); 728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int pmu_reset_deassert(struct reset_controller_dev *rc, unsigned long id) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct pmu_data *pmu = rcdev_to_pmu(rc); 808c2ecf20Sopenharmony_ci unsigned long flags; 818c2ecf20Sopenharmony_ci u32 val = BIT(id); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 848c2ecf20Sopenharmony_ci val |= readl_relaxed(pmu->pmc_base + PMC_SW_RST); 858c2ecf20Sopenharmony_ci writel_relaxed(val, pmu->pmc_base + PMC_SW_RST); 868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct reset_control_ops pmu_reset_ops = { 928c2ecf20Sopenharmony_ci .reset = pmu_reset_reset, 938c2ecf20Sopenharmony_ci .assert = pmu_reset_assert, 948c2ecf20Sopenharmony_ci .deassert = pmu_reset_deassert, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct reset_controller_dev pmu_reset __initdata = { 988c2ecf20Sopenharmony_ci .ops = &pmu_reset_ops, 998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1008c2ecf20Sopenharmony_ci .nr_resets = 32, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void __init pmu_reset_init(struct pmu_data *pmu) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pmu->reset = pmu_reset; 1088c2ecf20Sopenharmony_ci pmu->reset.of_node = pmu->of_node; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = reset_controller_register(&pmu->reset); 1118c2ecf20Sopenharmony_ci if (ret) 1128c2ecf20Sopenharmony_ci pr_err("pmu: %s failed: %d\n", "reset_controller_register", ret); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci#else 1158c2ecf20Sopenharmony_cistatic void __init pmu_reset_init(struct pmu_data *pmu) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci#endif 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct pmu_domain { 1218c2ecf20Sopenharmony_ci struct pmu_data *pmu; 1228c2ecf20Sopenharmony_ci u32 pwr_mask; 1238c2ecf20Sopenharmony_ci u32 rst_mask; 1248c2ecf20Sopenharmony_ci u32 iso_mask; 1258c2ecf20Sopenharmony_ci struct generic_pm_domain base; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define to_pmu_domain(dom) container_of(dom, struct pmu_domain, base) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * This deals with the "old" Marvell sequence of bringing a power domain 1328c2ecf20Sopenharmony_ci * down/up, which is: apply power, release reset, disable isolators. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Later devices apparantly use a different sequence: power up, disable 1358c2ecf20Sopenharmony_ci * isolators, assert repair signal, enable SRMA clock, enable AXI clock, 1368c2ecf20Sopenharmony_ci * enable module clock, deassert reset. 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Note: reading the assembly, it seems that the IO accessors have an 1398c2ecf20Sopenharmony_ci * unfortunate side-effect - they cause memory already read into registers 1408c2ecf20Sopenharmony_ci * for the if () to be re-read for the bit-set or bit-clear operation. 1418c2ecf20Sopenharmony_ci * The code is written to avoid this. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic int pmu_domain_power_off(struct generic_pm_domain *domain) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct pmu_domain *pmu_dom = to_pmu_domain(domain); 1468c2ecf20Sopenharmony_ci struct pmu_data *pmu = pmu_dom->pmu; 1478c2ecf20Sopenharmony_ci unsigned long flags; 1488c2ecf20Sopenharmony_ci unsigned int val; 1498c2ecf20Sopenharmony_ci void __iomem *pmu_base = pmu->pmu_base; 1508c2ecf20Sopenharmony_ci void __iomem *pmc_base = pmu->pmc_base; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Enable isolators */ 1558c2ecf20Sopenharmony_ci if (pmu_dom->iso_mask) { 1568c2ecf20Sopenharmony_ci val = ~pmu_dom->iso_mask; 1578c2ecf20Sopenharmony_ci val &= readl_relaxed(pmu_base + PMU_ISO); 1588c2ecf20Sopenharmony_ci writel_relaxed(val, pmu_base + PMU_ISO); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Reset unit */ 1628c2ecf20Sopenharmony_ci if (pmu_dom->rst_mask) { 1638c2ecf20Sopenharmony_ci val = ~pmu_dom->rst_mask; 1648c2ecf20Sopenharmony_ci val &= readl_relaxed(pmc_base + PMC_SW_RST); 1658c2ecf20Sopenharmony_ci writel_relaxed(val, pmc_base + PMC_SW_RST); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Power down */ 1698c2ecf20Sopenharmony_ci val = readl_relaxed(pmu_base + PMU_PWR) | pmu_dom->pwr_mask; 1708c2ecf20Sopenharmony_ci writel_relaxed(val, pmu_base + PMU_PWR); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int pmu_domain_power_on(struct generic_pm_domain *domain) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct pmu_domain *pmu_dom = to_pmu_domain(domain); 1808c2ecf20Sopenharmony_ci struct pmu_data *pmu = pmu_dom->pmu; 1818c2ecf20Sopenharmony_ci unsigned long flags; 1828c2ecf20Sopenharmony_ci unsigned int val; 1838c2ecf20Sopenharmony_ci void __iomem *pmu_base = pmu->pmu_base; 1848c2ecf20Sopenharmony_ci void __iomem *pmc_base = pmu->pmc_base; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Power on */ 1898c2ecf20Sopenharmony_ci val = ~pmu_dom->pwr_mask & readl_relaxed(pmu_base + PMU_PWR); 1908c2ecf20Sopenharmony_ci writel_relaxed(val, pmu_base + PMU_PWR); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Release reset */ 1938c2ecf20Sopenharmony_ci if (pmu_dom->rst_mask) { 1948c2ecf20Sopenharmony_ci val = pmu_dom->rst_mask; 1958c2ecf20Sopenharmony_ci val |= readl_relaxed(pmc_base + PMC_SW_RST); 1968c2ecf20Sopenharmony_ci writel_relaxed(val, pmc_base + PMC_SW_RST); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Disable isolators */ 2008c2ecf20Sopenharmony_ci if (pmu_dom->iso_mask) { 2018c2ecf20Sopenharmony_ci val = pmu_dom->iso_mask; 2028c2ecf20Sopenharmony_ci val |= readl_relaxed(pmu_base + PMU_ISO); 2038c2ecf20Sopenharmony_ci writel_relaxed(val, pmu_base + PMU_ISO); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void __pmu_domain_register(struct pmu_domain *domain, 2128c2ecf20Sopenharmony_ci struct device_node *np) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci unsigned int val = readl_relaxed(domain->pmu->pmu_base + PMU_PWR); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci domain->base.power_off = pmu_domain_power_off; 2178c2ecf20Sopenharmony_ci domain->base.power_on = pmu_domain_power_on; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pm_genpd_init(&domain->base, NULL, !(val & domain->pwr_mask)); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (np) 2228c2ecf20Sopenharmony_ci of_genpd_add_provider_simple(np, &domain->base); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* PMU IRQ controller */ 2268c2ecf20Sopenharmony_cistatic void pmu_irq_handler(struct irq_desc *desc) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct pmu_data *pmu = irq_desc_get_handler_data(desc); 2298c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = pmu->irq_gc; 2308c2ecf20Sopenharmony_ci struct irq_domain *domain = pmu->irq_domain; 2318c2ecf20Sopenharmony_ci void __iomem *base = gc->reg_base; 2328c2ecf20Sopenharmony_ci u32 stat = readl_relaxed(base + PMC_IRQ_CAUSE) & gc->mask_cache; 2338c2ecf20Sopenharmony_ci u32 done = ~0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (stat == 0) { 2368c2ecf20Sopenharmony_ci handle_bad_irq(desc); 2378c2ecf20Sopenharmony_ci return; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci while (stat) { 2418c2ecf20Sopenharmony_ci u32 hwirq = fls(stat) - 1; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci stat &= ~(1 << hwirq); 2448c2ecf20Sopenharmony_ci done &= ~(1 << hwirq); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(domain, hwirq)); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * The PMU mask register is not RW0C: it is RW. This means that 2518c2ecf20Sopenharmony_ci * the bits take whatever value is written to them; if you write 2528c2ecf20Sopenharmony_ci * a '1', you will set the interrupt. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * Unfortunately this means there is NO race free way to clear 2558c2ecf20Sopenharmony_ci * these interrupts. 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * So, let's structure the code so that the window is as small as 2588c2ecf20Sopenharmony_ci * possible. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci irq_gc_lock(gc); 2618c2ecf20Sopenharmony_ci done &= readl_relaxed(base + PMC_IRQ_CAUSE); 2628c2ecf20Sopenharmony_ci writel_relaxed(done, base + PMC_IRQ_CAUSE); 2638c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int __init dove_init_pmu_irq(struct pmu_data *pmu, int irq) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci const char *name = "pmu_irq"; 2698c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 2708c2ecf20Sopenharmony_ci struct irq_domain *domain; 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* mask and clear all interrupts */ 2748c2ecf20Sopenharmony_ci writel(0, pmu->pmc_base + PMC_IRQ_MASK); 2758c2ecf20Sopenharmony_ci writel(0, pmu->pmc_base + PMC_IRQ_CAUSE); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci domain = irq_domain_add_linear(pmu->of_node, NR_PMU_IRQS, 2788c2ecf20Sopenharmony_ci &irq_generic_chip_ops, NULL); 2798c2ecf20Sopenharmony_ci if (!domain) { 2808c2ecf20Sopenharmony_ci pr_err("%s: unable to add irq domain\n", name); 2818c2ecf20Sopenharmony_ci return -ENOMEM; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = irq_alloc_domain_generic_chips(domain, NR_PMU_IRQS, 1, name, 2858c2ecf20Sopenharmony_ci handle_level_irq, 2868c2ecf20Sopenharmony_ci IRQ_NOREQUEST | IRQ_NOPROBE, 0, 2878c2ecf20Sopenharmony_ci IRQ_GC_INIT_MASK_CACHE); 2888c2ecf20Sopenharmony_ci if (ret) { 2898c2ecf20Sopenharmony_ci pr_err("%s: unable to alloc irq domain gc: %d\n", name, ret); 2908c2ecf20Sopenharmony_ci irq_domain_remove(domain); 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, 0); 2958c2ecf20Sopenharmony_ci gc->reg_base = pmu->pmc_base; 2968c2ecf20Sopenharmony_ci gc->chip_types[0].regs.mask = PMC_IRQ_MASK; 2978c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 2988c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pmu->irq_domain = domain; 3018c2ecf20Sopenharmony_ci pmu->irq_gc = gc; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci irq_set_handler_data(irq, pmu); 3048c2ecf20Sopenharmony_ci irq_set_chained_handler(irq, pmu_irq_handler); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciint __init dove_init_pmu_legacy(const struct dove_pmu_initdata *initdata) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci const struct dove_pmu_domain_initdata *domain_initdata; 3128c2ecf20Sopenharmony_ci struct pmu_data *pmu; 3138c2ecf20Sopenharmony_ci int ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci pmu = kzalloc(sizeof(*pmu), GFP_KERNEL); 3168c2ecf20Sopenharmony_ci if (!pmu) 3178c2ecf20Sopenharmony_ci return -ENOMEM; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spin_lock_init(&pmu->lock); 3208c2ecf20Sopenharmony_ci pmu->pmc_base = initdata->pmc_base; 3218c2ecf20Sopenharmony_ci pmu->pmu_base = initdata->pmu_base; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pmu_reset_init(pmu); 3248c2ecf20Sopenharmony_ci for (domain_initdata = initdata->domains; domain_initdata->name; 3258c2ecf20Sopenharmony_ci domain_initdata++) { 3268c2ecf20Sopenharmony_ci struct pmu_domain *domain; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_KERNEL); 3298c2ecf20Sopenharmony_ci if (domain) { 3308c2ecf20Sopenharmony_ci domain->pmu = pmu; 3318c2ecf20Sopenharmony_ci domain->pwr_mask = domain_initdata->pwr_mask; 3328c2ecf20Sopenharmony_ci domain->rst_mask = domain_initdata->rst_mask; 3338c2ecf20Sopenharmony_ci domain->iso_mask = domain_initdata->iso_mask; 3348c2ecf20Sopenharmony_ci domain->base.name = domain_initdata->name; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci __pmu_domain_register(domain, NULL); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = dove_init_pmu_irq(pmu, initdata->irq); 3418c2ecf20Sopenharmony_ci if (ret) 3428c2ecf20Sopenharmony_ci pr_err("dove_init_pmu_irq() failed: %d\n", ret); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (pmu->irq_domain) 3458c2ecf20Sopenharmony_ci irq_domain_associate_many(pmu->irq_domain, 3468c2ecf20Sopenharmony_ci initdata->irq_domain_start, 3478c2ecf20Sopenharmony_ci 0, NR_PMU_IRQS); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* 3538c2ecf20Sopenharmony_ci * pmu: power-manager@d0000 { 3548c2ecf20Sopenharmony_ci * compatible = "marvell,dove-pmu"; 3558c2ecf20Sopenharmony_ci * reg = <0xd0000 0x8000> <0xd8000 0x8000>; 3568c2ecf20Sopenharmony_ci * interrupts = <33>; 3578c2ecf20Sopenharmony_ci * interrupt-controller; 3588c2ecf20Sopenharmony_ci * #reset-cells = 1; 3598c2ecf20Sopenharmony_ci * vpu_domain: vpu-domain { 3608c2ecf20Sopenharmony_ci * #power-domain-cells = <0>; 3618c2ecf20Sopenharmony_ci * marvell,pmu_pwr_mask = <0x00000008>; 3628c2ecf20Sopenharmony_ci * marvell,pmu_iso_mask = <0x00000001>; 3638c2ecf20Sopenharmony_ci * resets = <&pmu 16>; 3648c2ecf20Sopenharmony_ci * }; 3658c2ecf20Sopenharmony_ci * gpu_domain: gpu-domain { 3668c2ecf20Sopenharmony_ci * #power-domain-cells = <0>; 3678c2ecf20Sopenharmony_ci * marvell,pmu_pwr_mask = <0x00000004>; 3688c2ecf20Sopenharmony_ci * marvell,pmu_iso_mask = <0x00000002>; 3698c2ecf20Sopenharmony_ci * resets = <&pmu 18>; 3708c2ecf20Sopenharmony_ci * }; 3718c2ecf20Sopenharmony_ci * }; 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ciint __init dove_init_pmu(void) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct device_node *np_pmu, *domains_node, *np; 3768c2ecf20Sopenharmony_ci struct pmu_data *pmu; 3778c2ecf20Sopenharmony_ci int ret, parent_irq; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Lookup the PMU node */ 3808c2ecf20Sopenharmony_ci np_pmu = of_find_compatible_node(NULL, NULL, "marvell,dove-pmu"); 3818c2ecf20Sopenharmony_ci if (!np_pmu) 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci domains_node = of_get_child_by_name(np_pmu, "domains"); 3858c2ecf20Sopenharmony_ci if (!domains_node) { 3868c2ecf20Sopenharmony_ci pr_err("%pOFn: failed to find domains sub-node\n", np_pmu); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci pmu = kzalloc(sizeof(*pmu), GFP_KERNEL); 3918c2ecf20Sopenharmony_ci if (!pmu) 3928c2ecf20Sopenharmony_ci return -ENOMEM; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci spin_lock_init(&pmu->lock); 3958c2ecf20Sopenharmony_ci pmu->of_node = np_pmu; 3968c2ecf20Sopenharmony_ci pmu->pmc_base = of_iomap(pmu->of_node, 0); 3978c2ecf20Sopenharmony_ci pmu->pmu_base = of_iomap(pmu->of_node, 1); 3988c2ecf20Sopenharmony_ci if (!pmu->pmc_base || !pmu->pmu_base) { 3998c2ecf20Sopenharmony_ci pr_err("%pOFn: failed to map PMU\n", np_pmu); 4008c2ecf20Sopenharmony_ci iounmap(pmu->pmu_base); 4018c2ecf20Sopenharmony_ci iounmap(pmu->pmc_base); 4028c2ecf20Sopenharmony_ci kfree(pmu); 4038c2ecf20Sopenharmony_ci return -ENOMEM; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci pmu_reset_init(pmu); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for_each_available_child_of_node(domains_node, np) { 4098c2ecf20Sopenharmony_ci struct of_phandle_args args; 4108c2ecf20Sopenharmony_ci struct pmu_domain *domain; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!domain) 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci domain->pmu = pmu; 4178c2ecf20Sopenharmony_ci domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np); 4188c2ecf20Sopenharmony_ci if (!domain->base.name) { 4198c2ecf20Sopenharmony_ci kfree(domain); 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci of_property_read_u32(np, "marvell,pmu_pwr_mask", 4248c2ecf20Sopenharmony_ci &domain->pwr_mask); 4258c2ecf20Sopenharmony_ci of_property_read_u32(np, "marvell,pmu_iso_mask", 4268c2ecf20Sopenharmony_ci &domain->iso_mask); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* 4298c2ecf20Sopenharmony_ci * We parse the reset controller property directly here 4308c2ecf20Sopenharmony_ci * to ensure that we can operate when the reset controller 4318c2ecf20Sopenharmony_ci * support is not configured into the kernel. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_args(np, "resets", "#reset-cells", 4348c2ecf20Sopenharmony_ci 0, &args); 4358c2ecf20Sopenharmony_ci if (ret == 0) { 4368c2ecf20Sopenharmony_ci if (args.np == pmu->of_node) 4378c2ecf20Sopenharmony_ci domain->rst_mask = BIT(args.args[0]); 4388c2ecf20Sopenharmony_ci of_node_put(args.np); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci __pmu_domain_register(domain, np); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Loss of the interrupt controller is not a fatal error. */ 4458c2ecf20Sopenharmony_ci parent_irq = irq_of_parse_and_map(pmu->of_node, 0); 4468c2ecf20Sopenharmony_ci if (!parent_irq) { 4478c2ecf20Sopenharmony_ci pr_err("%pOFn: no interrupt specified\n", np_pmu); 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci ret = dove_init_pmu_irq(pmu, parent_irq); 4508c2ecf20Sopenharmony_ci if (ret) 4518c2ecf20Sopenharmony_ci pr_err("dove_init_pmu_irq() failed: %d\n", ret); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 456