xref: /linux/drivers/soc/apple/apple-pmgr-misc.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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