1*fef9350cSHector Martin // SPDX-License-Identifier: GPL-2.0-only OR MIT 2*fef9350cSHector Martin /* 3*fef9350cSHector Martin * Apple SoC PMGR device power state driver 4*fef9350cSHector Martin * 5*fef9350cSHector Martin * Copyright The Asahi Linux Contributors 6*fef9350cSHector Martin */ 7*fef9350cSHector Martin 8*fef9350cSHector Martin #include <linux/bitfield.h> 9*fef9350cSHector Martin #include <linux/bitops.h> 10*fef9350cSHector Martin #include <linux/err.h> 11*fef9350cSHector Martin #include <linux/io.h> 12*fef9350cSHector Martin #include <linux/module.h> 13*fef9350cSHector Martin #include <linux/of.h> 14*fef9350cSHector Martin #include <linux/platform_device.h> 15*fef9350cSHector Martin 16*fef9350cSHector Martin #define APPLE_CLKGEN_PSTATE 0 17*fef9350cSHector Martin #define APPLE_CLKGEN_PSTATE_DESIRED GENMASK(3, 0) 18*fef9350cSHector Martin 19*fef9350cSHector Martin #define DCS_DEV_PSTATE_MIN_T600X 7 20*fef9350cSHector Martin #define SYS_DEV_PSTATE_SUSPEND 1 21*fef9350cSHector Martin 22*fef9350cSHector Martin enum sys_device { 23*fef9350cSHector Martin DEV_FABRIC, 24*fef9350cSHector Martin DEV_DCS, 25*fef9350cSHector Martin DEV_MAX, 26*fef9350cSHector Martin }; 27*fef9350cSHector Martin 28*fef9350cSHector Martin struct apple_pmgr_sys_device { 29*fef9350cSHector Martin void __iomem *base; 30*fef9350cSHector Martin u32 active_state; 31*fef9350cSHector Martin u32 suspend_state; 32*fef9350cSHector Martin }; 33*fef9350cSHector Martin 34*fef9350cSHector Martin struct apple_pmgr_misc_hw { 35*fef9350cSHector Martin u32 dev_min_ps[DEV_MAX]; 36*fef9350cSHector Martin }; 37*fef9350cSHector Martin 38*fef9350cSHector Martin struct apple_pmgr_misc { 39*fef9350cSHector Martin struct device *dev; 40*fef9350cSHector Martin struct apple_pmgr_sys_device devices[DEV_MAX]; 41*fef9350cSHector Martin }; 42*fef9350cSHector Martin 43*fef9350cSHector Martin static void apple_pmgr_sys_dev_set_pstate(struct apple_pmgr_misc *misc, 44*fef9350cSHector Martin enum sys_device dev, bool active) 45*fef9350cSHector Martin { 46*fef9350cSHector Martin u32 pstate; 47*fef9350cSHector Martin u32 val; 48*fef9350cSHector Martin 49*fef9350cSHector Martin if (!misc->devices[dev].base) 50*fef9350cSHector Martin return; 51*fef9350cSHector Martin 52*fef9350cSHector Martin if (active) 53*fef9350cSHector Martin pstate = misc->devices[dev].active_state; 54*fef9350cSHector Martin else 55*fef9350cSHector Martin pstate = misc->devices[dev].suspend_state; 56*fef9350cSHector Martin 57*fef9350cSHector Martin dev_dbg(misc->dev, "set %d ps to pstate %d\n", dev, pstate); 58*fef9350cSHector Martin 59*fef9350cSHector Martin val = readl_relaxed(misc->devices[dev].base + APPLE_CLKGEN_PSTATE); 60*fef9350cSHector Martin FIELD_MODIFY(APPLE_CLKGEN_PSTATE_DESIRED, &val, pstate); 61*fef9350cSHector Martin writel_relaxed(val, misc->devices[dev].base + APPLE_CLKGEN_PSTATE); 62*fef9350cSHector Martin } 63*fef9350cSHector Martin 64*fef9350cSHector Martin static int __maybe_unused apple_pmgr_misc_suspend_noirq(struct device *dev) 65*fef9350cSHector Martin { 66*fef9350cSHector Martin struct apple_pmgr_misc *misc = dev_get_drvdata(dev); 67*fef9350cSHector Martin int i; 68*fef9350cSHector Martin 69*fef9350cSHector Martin for (i = 0; i < DEV_MAX; i++) 70*fef9350cSHector Martin apple_pmgr_sys_dev_set_pstate(misc, i, false); 71*fef9350cSHector Martin 72*fef9350cSHector Martin return 0; 73*fef9350cSHector Martin } 74*fef9350cSHector Martin 75*fef9350cSHector Martin static int __maybe_unused apple_pmgr_misc_resume_noirq(struct device *dev) 76*fef9350cSHector Martin { 77*fef9350cSHector Martin struct apple_pmgr_misc *misc = dev_get_drvdata(dev); 78*fef9350cSHector Martin int i; 79*fef9350cSHector Martin 80*fef9350cSHector Martin for (i = 0; i < DEV_MAX; i++) 81*fef9350cSHector Martin apple_pmgr_sys_dev_set_pstate(misc, i, true); 82*fef9350cSHector Martin 83*fef9350cSHector Martin return 0; 84*fef9350cSHector Martin } 85*fef9350cSHector Martin 86*fef9350cSHector Martin static int apple_pmgr_init_device(struct apple_pmgr_misc *misc, 87*fef9350cSHector Martin const struct apple_pmgr_misc_hw *hw, 88*fef9350cSHector Martin enum sys_device dev, 89*fef9350cSHector Martin const char *device_name) 90*fef9350cSHector Martin { 91*fef9350cSHector Martin void __iomem *base; 92*fef9350cSHector Martin char name[32]; 93*fef9350cSHector Martin u32 val; 94*fef9350cSHector Martin 95*fef9350cSHector Martin snprintf(name, sizeof(name), "%s-ps", device_name); 96*fef9350cSHector Martin 97*fef9350cSHector Martin base = devm_platform_ioremap_resource_byname( 98*fef9350cSHector Martin to_platform_device(misc->dev), name); 99*fef9350cSHector Martin if (IS_ERR(base)) 100*fef9350cSHector Martin return PTR_ERR(base); 101*fef9350cSHector Martin 102*fef9350cSHector Martin val = readl_relaxed(base + APPLE_CLKGEN_PSTATE); 103*fef9350cSHector Martin 104*fef9350cSHector Martin misc->devices[dev].base = base; 105*fef9350cSHector Martin misc->devices[dev].active_state = 106*fef9350cSHector Martin FIELD_GET(APPLE_CLKGEN_PSTATE_DESIRED, val); 107*fef9350cSHector Martin misc->devices[dev].suspend_state = hw->dev_min_ps[dev]; 108*fef9350cSHector Martin 109*fef9350cSHector Martin return 0; 110*fef9350cSHector Martin } 111*fef9350cSHector Martin 112*fef9350cSHector Martin static int apple_pmgr_misc_probe(struct platform_device *pdev) 113*fef9350cSHector Martin { 114*fef9350cSHector Martin struct device *dev = &pdev->dev; 115*fef9350cSHector Martin const struct apple_pmgr_misc_hw *hw; 116*fef9350cSHector Martin struct apple_pmgr_misc *misc; 117*fef9350cSHector Martin int ret; 118*fef9350cSHector Martin 119*fef9350cSHector Martin misc = devm_kzalloc(dev, sizeof(*misc), GFP_KERNEL); 120*fef9350cSHector Martin if (!misc) 121*fef9350cSHector Martin return -ENOMEM; 122*fef9350cSHector Martin 123*fef9350cSHector Martin misc->dev = dev; 124*fef9350cSHector Martin hw = of_device_get_match_data(dev); 125*fef9350cSHector Martin if (!hw) 126*fef9350cSHector Martin return -EINVAL; 127*fef9350cSHector Martin 128*fef9350cSHector Martin ret = apple_pmgr_init_device(misc, hw, DEV_FABRIC, "fabric"); 129*fef9350cSHector Martin if (ret) 130*fef9350cSHector Martin return ret; 131*fef9350cSHector Martin 132*fef9350cSHector Martin ret = apple_pmgr_init_device(misc, hw, DEV_DCS, "dcs"); 133*fef9350cSHector Martin if (ret) 134*fef9350cSHector Martin return ret; 135*fef9350cSHector Martin 136*fef9350cSHector Martin platform_set_drvdata(pdev, misc); 137*fef9350cSHector Martin 138*fef9350cSHector Martin return 0; 139*fef9350cSHector Martin } 140*fef9350cSHector Martin 141*fef9350cSHector Martin static const struct apple_pmgr_misc_hw apple_pmgr_misc_hw_t600x = { 142*fef9350cSHector Martin .dev_min_ps = { 143*fef9350cSHector Martin [DEV_FABRIC] = SYS_DEV_PSTATE_SUSPEND, 144*fef9350cSHector Martin [DEV_DCS] = DCS_DEV_PSTATE_MIN_T600X, 145*fef9350cSHector Martin }, 146*fef9350cSHector Martin }; 147*fef9350cSHector Martin 148*fef9350cSHector Martin static const struct apple_pmgr_misc_hw apple_pmgr_misc_hw_t602x = { 149*fef9350cSHector Martin .dev_min_ps = { 150*fef9350cSHector Martin [DEV_FABRIC] = SYS_DEV_PSTATE_SUSPEND, 151*fef9350cSHector Martin [DEV_DCS] = SYS_DEV_PSTATE_SUSPEND, 152*fef9350cSHector Martin }, 153*fef9350cSHector Martin }; 154*fef9350cSHector Martin 155*fef9350cSHector Martin static const struct of_device_id apple_pmgr_misc_of_match[] = { 156*fef9350cSHector Martin { .compatible = "apple,t6000-pmgr-misc", .data = &apple_pmgr_misc_hw_t600x }, 157*fef9350cSHector Martin { .compatible = "apple,t6020-pmgr-misc", .data = &apple_pmgr_misc_hw_t602x }, 158*fef9350cSHector Martin {} 159*fef9350cSHector Martin }; 160*fef9350cSHector Martin 161*fef9350cSHector Martin MODULE_DEVICE_TABLE(of, apple_pmgr_misc_of_match); 162*fef9350cSHector Martin 163*fef9350cSHector Martin static const struct dev_pm_ops apple_pmgr_misc_pm_ops = { 164*fef9350cSHector Martin SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(apple_pmgr_misc_suspend_noirq, 165*fef9350cSHector Martin apple_pmgr_misc_resume_noirq) 166*fef9350cSHector Martin }; 167*fef9350cSHector Martin 168*fef9350cSHector Martin static struct platform_driver apple_pmgr_misc_driver = { 169*fef9350cSHector Martin .probe = apple_pmgr_misc_probe, 170*fef9350cSHector Martin .driver = { 171*fef9350cSHector Martin .name = "apple-pmgr-misc", 172*fef9350cSHector Martin .of_match_table = apple_pmgr_misc_of_match, 173*fef9350cSHector Martin .pm = pm_ptr(&apple_pmgr_misc_pm_ops), 174*fef9350cSHector Martin }, 175*fef9350cSHector Martin }; 176*fef9350cSHector Martin 177*fef9350cSHector Martin MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); 178*fef9350cSHector Martin MODULE_DESCRIPTION("PMGR misc driver for Apple SoCs"); 179*fef9350cSHector Martin MODULE_LICENSE("GPL"); 180*fef9350cSHector Martin 181*fef9350cSHector Martin module_platform_driver(apple_pmgr_misc_driver); 182