18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Power domain driver for Broadcom BCM2835 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Broadcom 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <dt-bindings/soc/bcm2835-pm.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/bcm2835-pm.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 168c2ecf20Sopenharmony_ci#include <linux/reset-controller.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define PM_GNRIC 0x00 208c2ecf20Sopenharmony_ci#define PM_AUDIO 0x04 218c2ecf20Sopenharmony_ci#define PM_STATUS 0x18 228c2ecf20Sopenharmony_ci#define PM_RSTC 0x1c 238c2ecf20Sopenharmony_ci#define PM_RSTS 0x20 248c2ecf20Sopenharmony_ci#define PM_WDOG 0x24 258c2ecf20Sopenharmony_ci#define PM_PADS0 0x28 268c2ecf20Sopenharmony_ci#define PM_PADS2 0x2c 278c2ecf20Sopenharmony_ci#define PM_PADS3 0x30 288c2ecf20Sopenharmony_ci#define PM_PADS4 0x34 298c2ecf20Sopenharmony_ci#define PM_PADS5 0x38 308c2ecf20Sopenharmony_ci#define PM_PADS6 0x3c 318c2ecf20Sopenharmony_ci#define PM_CAM0 0x44 328c2ecf20Sopenharmony_ci#define PM_CAM0_LDOHPEN BIT(2) 338c2ecf20Sopenharmony_ci#define PM_CAM0_LDOLPEN BIT(1) 348c2ecf20Sopenharmony_ci#define PM_CAM0_CTRLEN BIT(0) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define PM_CAM1 0x48 378c2ecf20Sopenharmony_ci#define PM_CAM1_LDOHPEN BIT(2) 388c2ecf20Sopenharmony_ci#define PM_CAM1_LDOLPEN BIT(1) 398c2ecf20Sopenharmony_ci#define PM_CAM1_CTRLEN BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define PM_CCP2TX 0x4c 428c2ecf20Sopenharmony_ci#define PM_CCP2TX_LDOEN BIT(1) 438c2ecf20Sopenharmony_ci#define PM_CCP2TX_CTRLEN BIT(0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PM_DSI0 0x50 468c2ecf20Sopenharmony_ci#define PM_DSI0_LDOHPEN BIT(2) 478c2ecf20Sopenharmony_ci#define PM_DSI0_LDOLPEN BIT(1) 488c2ecf20Sopenharmony_ci#define PM_DSI0_CTRLEN BIT(0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define PM_DSI1 0x54 518c2ecf20Sopenharmony_ci#define PM_DSI1_LDOHPEN BIT(2) 528c2ecf20Sopenharmony_ci#define PM_DSI1_LDOLPEN BIT(1) 538c2ecf20Sopenharmony_ci#define PM_DSI1_CTRLEN BIT(0) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define PM_HDMI 0x58 568c2ecf20Sopenharmony_ci#define PM_HDMI_RSTDR BIT(19) 578c2ecf20Sopenharmony_ci#define PM_HDMI_LDOPD BIT(1) 588c2ecf20Sopenharmony_ci#define PM_HDMI_CTRLEN BIT(0) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define PM_USB 0x5c 618c2ecf20Sopenharmony_ci/* The power gates must be enabled with this bit before enabling the LDO in the 628c2ecf20Sopenharmony_ci * USB block. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci#define PM_USB_CTRLEN BIT(0) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define PM_PXLDO 0x60 678c2ecf20Sopenharmony_ci#define PM_PXBG 0x64 688c2ecf20Sopenharmony_ci#define PM_DFT 0x68 698c2ecf20Sopenharmony_ci#define PM_SMPS 0x6c 708c2ecf20Sopenharmony_ci#define PM_XOSC 0x70 718c2ecf20Sopenharmony_ci#define PM_SPAREW 0x74 728c2ecf20Sopenharmony_ci#define PM_SPARER 0x78 738c2ecf20Sopenharmony_ci#define PM_AVS_RSTDR 0x7c 748c2ecf20Sopenharmony_ci#define PM_AVS_STAT 0x80 758c2ecf20Sopenharmony_ci#define PM_AVS_EVENT 0x84 768c2ecf20Sopenharmony_ci#define PM_AVS_INTEN 0x88 778c2ecf20Sopenharmony_ci#define PM_DUMMY 0xfc 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define PM_IMAGE 0x108 808c2ecf20Sopenharmony_ci#define PM_GRAFX 0x10c 818c2ecf20Sopenharmony_ci#define PM_PROC 0x110 828c2ecf20Sopenharmony_ci#define PM_ENAB BIT(12) 838c2ecf20Sopenharmony_ci#define PM_ISPRSTN BIT(8) 848c2ecf20Sopenharmony_ci#define PM_H264RSTN BIT(7) 858c2ecf20Sopenharmony_ci#define PM_PERIRSTN BIT(6) 868c2ecf20Sopenharmony_ci#define PM_V3DRSTN BIT(6) 878c2ecf20Sopenharmony_ci#define PM_ISFUNC BIT(5) 888c2ecf20Sopenharmony_ci#define PM_MRDONE BIT(4) 898c2ecf20Sopenharmony_ci#define PM_MEMREP BIT(3) 908c2ecf20Sopenharmony_ci#define PM_ISPOW BIT(2) 918c2ecf20Sopenharmony_ci#define PM_POWOK BIT(1) 928c2ecf20Sopenharmony_ci#define PM_POWUP BIT(0) 938c2ecf20Sopenharmony_ci#define PM_INRUSH_SHIFT 13 948c2ecf20Sopenharmony_ci#define PM_INRUSH_3_5_MA 0 958c2ecf20Sopenharmony_ci#define PM_INRUSH_5_MA 1 968c2ecf20Sopenharmony_ci#define PM_INRUSH_10_MA 2 978c2ecf20Sopenharmony_ci#define PM_INRUSH_20_MA 3 988c2ecf20Sopenharmony_ci#define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define PM_PASSWORD 0x5a000000 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define PM_WDOG_TIME_SET 0x000fffff 1038c2ecf20Sopenharmony_ci#define PM_RSTC_WRCFG_CLR 0xffffffcf 1048c2ecf20Sopenharmony_ci#define PM_RSTS_HADWRH_SET 0x00000040 1058c2ecf20Sopenharmony_ci#define PM_RSTC_WRCFG_SET 0x00000030 1068c2ecf20Sopenharmony_ci#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 1078c2ecf20Sopenharmony_ci#define PM_RSTC_RESET 0x00000102 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define PM_READ(reg) readl(power->base + (reg)) 1108c2ecf20Sopenharmony_ci#define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg)) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define ASB_BRDG_VERSION 0x00 1138c2ecf20Sopenharmony_ci#define ASB_CPR_CTRL 0x04 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define ASB_V3D_S_CTRL 0x08 1168c2ecf20Sopenharmony_ci#define ASB_V3D_M_CTRL 0x0c 1178c2ecf20Sopenharmony_ci#define ASB_ISP_S_CTRL 0x10 1188c2ecf20Sopenharmony_ci#define ASB_ISP_M_CTRL 0x14 1198c2ecf20Sopenharmony_ci#define ASB_H264_S_CTRL 0x18 1208c2ecf20Sopenharmony_ci#define ASB_H264_M_CTRL 0x1c 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define ASB_REQ_STOP BIT(0) 1238c2ecf20Sopenharmony_ci#define ASB_ACK BIT(1) 1248c2ecf20Sopenharmony_ci#define ASB_EMPTY BIT(2) 1258c2ecf20Sopenharmony_ci#define ASB_FULL BIT(3) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define ASB_AXI_BRDG_ID 0x20 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define ASB_READ(reg) readl(power->asb + (reg)) 1308c2ecf20Sopenharmony_ci#define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg)) 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct bcm2835_power_domain { 1338c2ecf20Sopenharmony_ci struct generic_pm_domain base; 1348c2ecf20Sopenharmony_ci struct bcm2835_power *power; 1358c2ecf20Sopenharmony_ci u32 domain; 1368c2ecf20Sopenharmony_ci struct clk *clk; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct bcm2835_power { 1408c2ecf20Sopenharmony_ci struct device *dev; 1418c2ecf20Sopenharmony_ci /* PM registers. */ 1428c2ecf20Sopenharmony_ci void __iomem *base; 1438c2ecf20Sopenharmony_ci /* AXI Async bridge registers. */ 1448c2ecf20Sopenharmony_ci void __iomem *asb; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci struct genpd_onecell_data pd_xlate; 1478c2ecf20Sopenharmony_ci struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; 1488c2ecf20Sopenharmony_ci struct reset_controller_dev reset; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u64 start; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!reg) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci start = ktime_get_ns(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Enable the module's async AXI bridges. */ 1618c2ecf20Sopenharmony_ci ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); 1628c2ecf20Sopenharmony_ci while (ASB_READ(reg) & ASB_ACK) { 1638c2ecf20Sopenharmony_ci cpu_relax(); 1648c2ecf20Sopenharmony_ci if (ktime_get_ns() - start >= 1000) 1658c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci u64 start; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!reg) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci start = ktime_get_ns(); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Enable the module's async AXI bridges. */ 1818c2ecf20Sopenharmony_ci ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP); 1828c2ecf20Sopenharmony_ci while (!(ASB_READ(reg) & ASB_ACK)) { 1838c2ecf20Sopenharmony_ci cpu_relax(); 1848c2ecf20Sopenharmony_ci if (ktime_get_ns() - start >= 1000) 1858c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Enable functional isolation */ 1968c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Enable electrical isolation */ 1998c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Open the power switches. */ 2028c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 2108c2ecf20Sopenharmony_ci struct device *dev = power->dev; 2118c2ecf20Sopenharmony_ci u64 start; 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci int inrush; 2148c2ecf20Sopenharmony_ci bool powok; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* If it was already powered on by the fw, leave it that way. */ 2178c2ecf20Sopenharmony_ci if (PM_READ(pm_reg) & PM_POWUP) 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Enable power. Allowing too much current at once may result 2218c2ecf20Sopenharmony_ci * in POWOK never getting set, so start low and ramp it up as 2228c2ecf20Sopenharmony_ci * necessary to succeed. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci powok = false; 2258c2ecf20Sopenharmony_ci for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) { 2268c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, 2278c2ecf20Sopenharmony_ci (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | 2288c2ecf20Sopenharmony_ci (inrush << PM_INRUSH_SHIFT) | 2298c2ecf20Sopenharmony_ci PM_POWUP); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci start = ktime_get_ns(); 2328c2ecf20Sopenharmony_ci while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) { 2338c2ecf20Sopenharmony_ci cpu_relax(); 2348c2ecf20Sopenharmony_ci if (ktime_get_ns() - start >= 3000) 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci if (!powok) { 2398c2ecf20Sopenharmony_ci dev_err(dev, "Timeout waiting for %s power OK\n", 2408c2ecf20Sopenharmony_ci pd->base.name); 2418c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2428c2ecf20Sopenharmony_ci goto err_disable_powup; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Disable electrical isolation */ 2468c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Repair memory */ 2498c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); 2508c2ecf20Sopenharmony_ci start = ktime_get_ns(); 2518c2ecf20Sopenharmony_ci while (!(PM_READ(pm_reg) & PM_MRDONE)) { 2528c2ecf20Sopenharmony_ci cpu_relax(); 2538c2ecf20Sopenharmony_ci if (ktime_get_ns() - start >= 1000) { 2548c2ecf20Sopenharmony_ci dev_err(dev, "Timeout waiting for %s memory repair\n", 2558c2ecf20Sopenharmony_ci pd->base.name); 2568c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2578c2ecf20Sopenharmony_ci goto err_disable_ispow; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Disable functional isolation */ 2628c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cierr_disable_ispow: 2678c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); 2688c2ecf20Sopenharmony_cierr_disable_powup: 2698c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); 2708c2ecf20Sopenharmony_ci return ret; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int bcm2835_asb_power_on(struct bcm2835_power_domain *pd, 2748c2ecf20Sopenharmony_ci u32 pm_reg, 2758c2ecf20Sopenharmony_ci u32 asb_m_reg, 2768c2ecf20Sopenharmony_ci u32 asb_s_reg, 2778c2ecf20Sopenharmony_ci u32 reset_flags) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pd->clk); 2838c2ecf20Sopenharmony_ci if (ret) { 2848c2ecf20Sopenharmony_ci dev_err(power->dev, "Failed to enable clock for %s\n", 2858c2ecf20Sopenharmony_ci pd->base.name); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Wait 32 clocks for reset to propagate, 1 us will be enough */ 2908c2ecf20Sopenharmony_ci udelay(1); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci clk_disable_unprepare(pd->clk); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Deassert the resets. */ 2958c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pd->clk); 2988c2ecf20Sopenharmony_ci if (ret) { 2998c2ecf20Sopenharmony_ci dev_err(power->dev, "Failed to enable clock for %s\n", 3008c2ecf20Sopenharmony_ci pd->base.name); 3018c2ecf20Sopenharmony_ci goto err_enable_resets; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ret = bcm2835_asb_enable(power, asb_m_reg); 3058c2ecf20Sopenharmony_ci if (ret) { 3068c2ecf20Sopenharmony_ci dev_err(power->dev, "Failed to enable ASB master for %s\n", 3078c2ecf20Sopenharmony_ci pd->base.name); 3088c2ecf20Sopenharmony_ci goto err_disable_clk; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci ret = bcm2835_asb_enable(power, asb_s_reg); 3118c2ecf20Sopenharmony_ci if (ret) { 3128c2ecf20Sopenharmony_ci dev_err(power->dev, "Failed to enable ASB slave for %s\n", 3138c2ecf20Sopenharmony_ci pd->base.name); 3148c2ecf20Sopenharmony_ci goto err_disable_asb_master; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cierr_disable_asb_master: 3208c2ecf20Sopenharmony_ci bcm2835_asb_disable(power, asb_m_reg); 3218c2ecf20Sopenharmony_cierr_disable_clk: 3228c2ecf20Sopenharmony_ci clk_disable_unprepare(pd->clk); 3238c2ecf20Sopenharmony_cierr_enable_resets: 3248c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int bcm2835_asb_power_off(struct bcm2835_power_domain *pd, 3298c2ecf20Sopenharmony_ci u32 pm_reg, 3308c2ecf20Sopenharmony_ci u32 asb_m_reg, 3318c2ecf20Sopenharmony_ci u32 asb_s_reg, 3328c2ecf20Sopenharmony_ci u32 reset_flags) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = bcm2835_asb_disable(power, asb_s_reg); 3388c2ecf20Sopenharmony_ci if (ret) { 3398c2ecf20Sopenharmony_ci dev_warn(power->dev, "Failed to disable ASB slave for %s\n", 3408c2ecf20Sopenharmony_ci pd->base.name); 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci ret = bcm2835_asb_disable(power, asb_m_reg); 3448c2ecf20Sopenharmony_ci if (ret) { 3458c2ecf20Sopenharmony_ci dev_warn(power->dev, "Failed to disable ASB master for %s\n", 3468c2ecf20Sopenharmony_ci pd->base.name); 3478c2ecf20Sopenharmony_ci bcm2835_asb_enable(power, asb_s_reg); 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci clk_disable_unprepare(pd->clk); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Assert the resets. */ 3548c2ecf20Sopenharmony_ci PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct bcm2835_power_domain *pd = 3628c2ecf20Sopenharmony_ci container_of(domain, struct bcm2835_power_domain, base); 3638c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (pd->domain) { 3668c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_GRAFX: 3678c2ecf20Sopenharmony_ci return bcm2835_power_power_on(pd, PM_GRAFX); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_GRAFX_V3D: 3708c2ecf20Sopenharmony_ci return bcm2835_asb_power_on(pd, PM_GRAFX, 3718c2ecf20Sopenharmony_ci ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, 3728c2ecf20Sopenharmony_ci PM_V3DRSTN); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE: 3758c2ecf20Sopenharmony_ci return bcm2835_power_power_on(pd, PM_IMAGE); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_PERI: 3788c2ecf20Sopenharmony_ci return bcm2835_asb_power_on(pd, PM_IMAGE, 3798c2ecf20Sopenharmony_ci 0, 0, 3808c2ecf20Sopenharmony_ci PM_PERIRSTN); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_ISP: 3838c2ecf20Sopenharmony_ci return bcm2835_asb_power_on(pd, PM_IMAGE, 3848c2ecf20Sopenharmony_ci ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, 3858c2ecf20Sopenharmony_ci PM_ISPRSTN); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_H264: 3888c2ecf20Sopenharmony_ci return bcm2835_asb_power_on(pd, PM_IMAGE, 3898c2ecf20Sopenharmony_ci ASB_H264_M_CTRL, ASB_H264_S_CTRL, 3908c2ecf20Sopenharmony_ci PM_H264RSTN); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_USB: 3938c2ecf20Sopenharmony_ci PM_WRITE(PM_USB, PM_USB_CTRLEN); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_DSI0: 3978c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); 3988c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_DSI1: 4028c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); 4038c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_CCP2TX: 4078c2ecf20Sopenharmony_ci PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); 4088c2ecf20Sopenharmony_ci PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_HDMI: 4128c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); 4138c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); 4148c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); 4158c2ecf20Sopenharmony_ci usleep_range(100, 200); 4168c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci dev_err(power->dev, "Invalid domain %d\n", pd->domain); 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct bcm2835_power_domain *pd = 4288c2ecf20Sopenharmony_ci container_of(domain, struct bcm2835_power_domain, base); 4298c2ecf20Sopenharmony_ci struct bcm2835_power *power = pd->power; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci switch (pd->domain) { 4328c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_GRAFX: 4338c2ecf20Sopenharmony_ci return bcm2835_power_power_off(pd, PM_GRAFX); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_GRAFX_V3D: 4368c2ecf20Sopenharmony_ci return bcm2835_asb_power_off(pd, PM_GRAFX, 4378c2ecf20Sopenharmony_ci ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, 4388c2ecf20Sopenharmony_ci PM_V3DRSTN); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE: 4418c2ecf20Sopenharmony_ci return bcm2835_power_power_off(pd, PM_IMAGE); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_PERI: 4448c2ecf20Sopenharmony_ci return bcm2835_asb_power_off(pd, PM_IMAGE, 4458c2ecf20Sopenharmony_ci 0, 0, 4468c2ecf20Sopenharmony_ci PM_PERIRSTN); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_ISP: 4498c2ecf20Sopenharmony_ci return bcm2835_asb_power_off(pd, PM_IMAGE, 4508c2ecf20Sopenharmony_ci ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, 4518c2ecf20Sopenharmony_ci PM_ISPRSTN); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_IMAGE_H264: 4548c2ecf20Sopenharmony_ci return bcm2835_asb_power_off(pd, PM_IMAGE, 4558c2ecf20Sopenharmony_ci ASB_H264_M_CTRL, ASB_H264_S_CTRL, 4568c2ecf20Sopenharmony_ci PM_H264RSTN); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_USB: 4598c2ecf20Sopenharmony_ci PM_WRITE(PM_USB, 0); 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_DSI0: 4638c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); 4648c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI0, 0); 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_DSI1: 4688c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); 4698c2ecf20Sopenharmony_ci PM_WRITE(PM_DSI1, 0); 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_CCP2TX: 4738c2ecf20Sopenharmony_ci PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); 4748c2ecf20Sopenharmony_ci PM_WRITE(PM_CCP2TX, 0); 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci case BCM2835_POWER_DOMAIN_HDMI: 4788c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); 4798c2ecf20Sopenharmony_ci PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci dev_err(power->dev, "Invalid domain %d\n", pd->domain); 4848c2ecf20Sopenharmony_ci return -EINVAL; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int 4898c2ecf20Sopenharmony_cibcm2835_init_power_domain(struct bcm2835_power *power, 4908c2ecf20Sopenharmony_ci int pd_xlate_index, const char *name) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct device *dev = power->dev; 4938c2ecf20Sopenharmony_ci struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci dom->clk = devm_clk_get(dev->parent, name); 4968c2ecf20Sopenharmony_ci if (IS_ERR(dom->clk)) { 4978c2ecf20Sopenharmony_ci int ret = PTR_ERR(dom->clk); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Some domains don't have a clk, so make sure that we 5038c2ecf20Sopenharmony_ci * don't deref an error pointer later. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci dom->clk = NULL; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dom->base.name = name; 5098c2ecf20Sopenharmony_ci dom->base.power_on = bcm2835_power_pd_power_on; 5108c2ecf20Sopenharmony_ci dom->base.power_off = bcm2835_power_pd_power_off; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci dom->domain = pd_xlate_index; 5138c2ecf20Sopenharmony_ci dom->power = power; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* XXX: on/off at boot? */ 5168c2ecf20Sopenharmony_ci pm_genpd_init(&dom->base, NULL, true); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci power->pd_xlate.domains[pd_xlate_index] = &dom->base; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return 0; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/** bcm2835_reset_reset - Resets a block that has a reset line in the 5248c2ecf20Sopenharmony_ci * PM block. 5258c2ecf20Sopenharmony_ci * 5268c2ecf20Sopenharmony_ci * The consumer of the reset controller must have the power domain up 5278c2ecf20Sopenharmony_ci * -- there's no reset ability with the power domain down. To reset 5288c2ecf20Sopenharmony_ci * the sub-block, we just disable its access to memory through the 5298c2ecf20Sopenharmony_ci * ASB, reset, and re-enable. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_cistatic int bcm2835_reset_reset(struct reset_controller_dev *rcdev, 5328c2ecf20Sopenharmony_ci unsigned long id) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, 5358c2ecf20Sopenharmony_ci reset); 5368c2ecf20Sopenharmony_ci struct bcm2835_power_domain *pd; 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci switch (id) { 5408c2ecf20Sopenharmony_ci case BCM2835_RESET_V3D: 5418c2ecf20Sopenharmony_ci pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; 5428c2ecf20Sopenharmony_ci break; 5438c2ecf20Sopenharmony_ci case BCM2835_RESET_H264: 5448c2ecf20Sopenharmony_ci pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci case BCM2835_RESET_ISP: 5478c2ecf20Sopenharmony_ci pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci default: 5508c2ecf20Sopenharmony_ci dev_err(power->dev, "Bad reset id %ld\n", id); 5518c2ecf20Sopenharmony_ci return -EINVAL; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = bcm2835_power_pd_power_off(&pd->base); 5558c2ecf20Sopenharmony_ci if (ret) 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return bcm2835_power_pd_power_on(&pd->base); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int bcm2835_reset_status(struct reset_controller_dev *rcdev, 5628c2ecf20Sopenharmony_ci unsigned long id) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, 5658c2ecf20Sopenharmony_ci reset); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci switch (id) { 5688c2ecf20Sopenharmony_ci case BCM2835_RESET_V3D: 5698c2ecf20Sopenharmony_ci return !PM_READ(PM_GRAFX & PM_V3DRSTN); 5708c2ecf20Sopenharmony_ci case BCM2835_RESET_H264: 5718c2ecf20Sopenharmony_ci return !PM_READ(PM_IMAGE & PM_H264RSTN); 5728c2ecf20Sopenharmony_ci case BCM2835_RESET_ISP: 5738c2ecf20Sopenharmony_ci return !PM_READ(PM_IMAGE & PM_ISPRSTN); 5748c2ecf20Sopenharmony_ci default: 5758c2ecf20Sopenharmony_ci return -EINVAL; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic const struct reset_control_ops bcm2835_reset_ops = { 5808c2ecf20Sopenharmony_ci .reset = bcm2835_reset_reset, 5818c2ecf20Sopenharmony_ci .status = bcm2835_reset_status, 5828c2ecf20Sopenharmony_ci}; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic const char *const power_domain_names[] = { 5858c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", 5868c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_IMAGE] = "image", 5898c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", 5908c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", 5918c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_USB] = "usb", 5948c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", 5958c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", 5968c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_CAM0] = "cam0", 5978c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_CAM1] = "cam1", 5988c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", 5998c2ecf20Sopenharmony_ci [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int bcm2835_power_probe(struct platform_device *pdev) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); 6058c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6068c2ecf20Sopenharmony_ci struct bcm2835_power *power; 6078c2ecf20Sopenharmony_ci static const struct { 6088c2ecf20Sopenharmony_ci int parent, child; 6098c2ecf20Sopenharmony_ci } domain_deps[] = { 6108c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D }, 6118c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI }, 6128c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 }, 6138c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP }, 6148c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB }, 6158c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 }, 6168c2ecf20Sopenharmony_ci { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 }, 6178c2ecf20Sopenharmony_ci }; 6188c2ecf20Sopenharmony_ci int ret = 0, i; 6198c2ecf20Sopenharmony_ci u32 id; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); 6228c2ecf20Sopenharmony_ci if (!power) 6238c2ecf20Sopenharmony_ci return -ENOMEM; 6248c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, power); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci power->dev = dev; 6278c2ecf20Sopenharmony_ci power->base = pm->base; 6288c2ecf20Sopenharmony_ci power->asb = pm->asb; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci id = ASB_READ(ASB_AXI_BRDG_ID); 6318c2ecf20Sopenharmony_ci if (id != 0x62726467 /* "BRDG" */) { 6328c2ecf20Sopenharmony_ci dev_err(dev, "ASB register ID returned 0x%08x\n", id); 6338c2ecf20Sopenharmony_ci return -ENODEV; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci power->pd_xlate.domains = devm_kcalloc(dev, 6378c2ecf20Sopenharmony_ci ARRAY_SIZE(power_domain_names), 6388c2ecf20Sopenharmony_ci sizeof(*power->pd_xlate.domains), 6398c2ecf20Sopenharmony_ci GFP_KERNEL); 6408c2ecf20Sopenharmony_ci if (!power->pd_xlate.domains) 6418c2ecf20Sopenharmony_ci return -ENOMEM; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { 6468c2ecf20Sopenharmony_ci ret = bcm2835_init_power_domain(power, i, power_domain_names[i]); 6478c2ecf20Sopenharmony_ci if (ret) 6488c2ecf20Sopenharmony_ci goto fail; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(domain_deps); i++) { 6528c2ecf20Sopenharmony_ci pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base, 6538c2ecf20Sopenharmony_ci &power->domains[domain_deps[i].child].base); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci power->reset.owner = THIS_MODULE; 6578c2ecf20Sopenharmony_ci power->reset.nr_resets = BCM2835_RESET_COUNT; 6588c2ecf20Sopenharmony_ci power->reset.ops = &bcm2835_reset_ops; 6598c2ecf20Sopenharmony_ci power->reset.of_node = dev->parent->of_node; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = devm_reset_controller_register(dev, &power->reset); 6628c2ecf20Sopenharmony_ci if (ret) 6638c2ecf20Sopenharmony_ci goto fail; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci dev_info(dev, "Broadcom BCM2835 power domains driver"); 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cifail: 6718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { 6728c2ecf20Sopenharmony_ci struct generic_pm_domain *dom = &power->domains[i].base; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (dom->name) 6758c2ecf20Sopenharmony_ci pm_genpd_remove(dom); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci return ret; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int bcm2835_power_remove(struct platform_device *pdev) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic struct platform_driver bcm2835_power_driver = { 6868c2ecf20Sopenharmony_ci .probe = bcm2835_power_probe, 6878c2ecf20Sopenharmony_ci .remove = bcm2835_power_remove, 6888c2ecf20Sopenharmony_ci .driver = { 6898c2ecf20Sopenharmony_ci .name = "bcm2835-power", 6908c2ecf20Sopenharmony_ci }, 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_cimodule_platform_driver(bcm2835_power_driver); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); 6958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset"); 6968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 697