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