1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Intel CHT Whiskey Cove PMIC operation region driver |
4 | * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> |
5 | * |
6 | * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: |
7 | * Copyright (C) 2013-2015 Intel Corporation. All rights reserved. |
8 | */ |
9 | |
10 | #include <linux/acpi.h> |
11 | #include <linux/init.h> |
12 | #include <linux/mfd/intel_soc_pmic.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/regmap.h> |
15 | #include "intel_pmic.h" |
16 | |
17 | #define CHT_WC_V1P05A_CTRL 0x6e3b |
18 | #define CHT_WC_V1P15_CTRL 0x6e3c |
19 | #define CHT_WC_V1P05A_VSEL 0x6e3d |
20 | #define CHT_WC_V1P15_VSEL 0x6e3e |
21 | #define CHT_WC_V1P8A_CTRL 0x6e56 |
22 | #define CHT_WC_V1P8SX_CTRL 0x6e57 |
23 | #define CHT_WC_VDDQ_CTRL 0x6e58 |
24 | #define CHT_WC_V1P2A_CTRL 0x6e59 |
25 | #define CHT_WC_V1P2SX_CTRL 0x6e5a |
26 | #define CHT_WC_V1P8A_VSEL 0x6e5b |
27 | #define CHT_WC_VDDQ_VSEL 0x6e5c |
28 | #define CHT_WC_V2P8SX_CTRL 0x6e5d |
29 | #define CHT_WC_V3P3A_CTRL 0x6e5e |
30 | #define CHT_WC_V3P3SD_CTRL 0x6e5f |
31 | #define CHT_WC_VSDIO_CTRL 0x6e67 |
32 | #define CHT_WC_V3P3A_VSEL 0x6e68 |
33 | #define CHT_WC_VPROG1A_CTRL 0x6e90 |
34 | #define CHT_WC_VPROG1B_CTRL 0x6e91 |
35 | #define CHT_WC_VPROG1F_CTRL 0x6e95 |
36 | #define CHT_WC_VPROG2D_CTRL 0x6e99 |
37 | #define CHT_WC_VPROG3A_CTRL 0x6e9a |
38 | #define CHT_WC_VPROG3B_CTRL 0x6e9b |
39 | #define CHT_WC_VPROG4A_CTRL 0x6e9c |
40 | #define CHT_WC_VPROG4B_CTRL 0x6e9d |
41 | #define CHT_WC_VPROG4C_CTRL 0x6e9e |
42 | #define CHT_WC_VPROG4D_CTRL 0x6e9f |
43 | #define CHT_WC_VPROG5A_CTRL 0x6ea0 |
44 | #define CHT_WC_VPROG5B_CTRL 0x6ea1 |
45 | #define CHT_WC_VPROG6A_CTRL 0x6ea2 |
46 | #define CHT_WC_VPROG6B_CTRL 0x6ea3 |
47 | #define CHT_WC_VPROG1A_VSEL 0x6ec0 |
48 | #define CHT_WC_VPROG1B_VSEL 0x6ec1 |
49 | #define CHT_WC_V1P8SX_VSEL 0x6ec2 |
50 | #define CHT_WC_V1P2SX_VSEL 0x6ec3 |
51 | #define CHT_WC_V1P2A_VSEL 0x6ec4 |
52 | #define CHT_WC_VPROG1F_VSEL 0x6ec5 |
53 | #define CHT_WC_VSDIO_VSEL 0x6ec6 |
54 | #define CHT_WC_V2P8SX_VSEL 0x6ec7 |
55 | #define CHT_WC_V3P3SD_VSEL 0x6ec8 |
56 | #define CHT_WC_VPROG2D_VSEL 0x6ec9 |
57 | #define CHT_WC_VPROG3A_VSEL 0x6eca |
58 | #define CHT_WC_VPROG3B_VSEL 0x6ecb |
59 | #define CHT_WC_VPROG4A_VSEL 0x6ecc |
60 | #define CHT_WC_VPROG4B_VSEL 0x6ecd |
61 | #define CHT_WC_VPROG4C_VSEL 0x6ece |
62 | #define CHT_WC_VPROG4D_VSEL 0x6ecf |
63 | #define CHT_WC_VPROG5A_VSEL 0x6ed0 |
64 | #define CHT_WC_VPROG5B_VSEL 0x6ed1 |
65 | #define CHT_WC_VPROG6A_VSEL 0x6ed2 |
66 | #define CHT_WC_VPROG6B_VSEL 0x6ed3 |
67 | |
68 | /* |
69 | * Regulator support is based on the non upstream patch: |
70 | * "regulator: whiskey_cove: implements Whiskey Cove pmic VRF support" |
71 | * https://github.com/intel-aero/meta-intel-aero/blob/master/recipes-kernel/linux/linux-yocto/0019-regulator-whiskey_cove-implements-WhiskeyCove-pmic-V.patch |
72 | */ |
73 | static struct pmic_table power_table[] = { |
74 | { |
75 | .address = 0x0, |
76 | .reg = CHT_WC_V1P8A_CTRL, |
77 | .bit = 0x01, |
78 | }, /* V18A */ |
79 | { |
80 | .address = 0x04, |
81 | .reg = CHT_WC_V1P8SX_CTRL, |
82 | .bit = 0x07, |
83 | }, /* V18X */ |
84 | { |
85 | .address = 0x08, |
86 | .reg = CHT_WC_VDDQ_CTRL, |
87 | .bit = 0x01, |
88 | }, /* VDDQ */ |
89 | { |
90 | .address = 0x0c, |
91 | .reg = CHT_WC_V1P2A_CTRL, |
92 | .bit = 0x07, |
93 | }, /* V12A */ |
94 | { |
95 | .address = 0x10, |
96 | .reg = CHT_WC_V1P2SX_CTRL, |
97 | .bit = 0x07, |
98 | }, /* V12X */ |
99 | { |
100 | .address = 0x14, |
101 | .reg = CHT_WC_V2P8SX_CTRL, |
102 | .bit = 0x07, |
103 | }, /* V28X */ |
104 | { |
105 | .address = 0x18, |
106 | .reg = CHT_WC_V3P3A_CTRL, |
107 | .bit = 0x01, |
108 | }, /* V33A */ |
109 | { |
110 | .address = 0x1c, |
111 | .reg = CHT_WC_V3P3SD_CTRL, |
112 | .bit = 0x07, |
113 | }, /* V3SD */ |
114 | { |
115 | .address = 0x20, |
116 | .reg = CHT_WC_VSDIO_CTRL, |
117 | .bit = 0x07, |
118 | }, /* VSD */ |
119 | /* { |
120 | .address = 0x24, |
121 | .reg = ??, |
122 | .bit = ??, |
123 | }, ** VSW2 */ |
124 | /* { |
125 | .address = 0x28, |
126 | .reg = ??, |
127 | .bit = ??, |
128 | }, ** VSW1 */ |
129 | /* { |
130 | .address = 0x2c, |
131 | .reg = ??, |
132 | .bit = ??, |
133 | }, ** VUPY */ |
134 | /* { |
135 | .address = 0x30, |
136 | .reg = ??, |
137 | .bit = ??, |
138 | }, ** VRSO */ |
139 | { |
140 | .address = 0x34, |
141 | .reg = CHT_WC_VPROG1A_CTRL, |
142 | .bit = 0x07, |
143 | }, /* VP1A */ |
144 | { |
145 | .address = 0x38, |
146 | .reg = CHT_WC_VPROG1B_CTRL, |
147 | .bit = 0x07, |
148 | }, /* VP1B */ |
149 | { |
150 | .address = 0x3c, |
151 | .reg = CHT_WC_VPROG1F_CTRL, |
152 | .bit = 0x07, |
153 | }, /* VP1F */ |
154 | { |
155 | .address = 0x40, |
156 | .reg = CHT_WC_VPROG2D_CTRL, |
157 | .bit = 0x07, |
158 | }, /* VP2D */ |
159 | { |
160 | .address = 0x44, |
161 | .reg = CHT_WC_VPROG3A_CTRL, |
162 | .bit = 0x07, |
163 | }, /* VP3A */ |
164 | { |
165 | .address = 0x48, |
166 | .reg = CHT_WC_VPROG3B_CTRL, |
167 | .bit = 0x07, |
168 | }, /* VP3B */ |
169 | { |
170 | .address = 0x4c, |
171 | .reg = CHT_WC_VPROG4A_CTRL, |
172 | .bit = 0x07, |
173 | }, /* VP4A */ |
174 | { |
175 | .address = 0x50, |
176 | .reg = CHT_WC_VPROG4B_CTRL, |
177 | .bit = 0x07, |
178 | }, /* VP4B */ |
179 | { |
180 | .address = 0x54, |
181 | .reg = CHT_WC_VPROG4C_CTRL, |
182 | .bit = 0x07, |
183 | }, /* VP4C */ |
184 | { |
185 | .address = 0x58, |
186 | .reg = CHT_WC_VPROG4D_CTRL, |
187 | .bit = 0x07, |
188 | }, /* VP4D */ |
189 | { |
190 | .address = 0x5c, |
191 | .reg = CHT_WC_VPROG5A_CTRL, |
192 | .bit = 0x07, |
193 | }, /* VP5A */ |
194 | { |
195 | .address = 0x60, |
196 | .reg = CHT_WC_VPROG5B_CTRL, |
197 | .bit = 0x07, |
198 | }, /* VP5B */ |
199 | { |
200 | .address = 0x64, |
201 | .reg = CHT_WC_VPROG6A_CTRL, |
202 | .bit = 0x07, |
203 | }, /* VP6A */ |
204 | { |
205 | .address = 0x68, |
206 | .reg = CHT_WC_VPROG6B_CTRL, |
207 | .bit = 0x07, |
208 | }, /* VP6B */ |
209 | /* { |
210 | .address = 0x6c, |
211 | .reg = ??, |
212 | .bit = ??, |
213 | } ** VP7A */ |
214 | }; |
215 | |
216 | static int intel_cht_wc_pmic_get_power(struct regmap *regmap, int reg, |
217 | int bit, u64 *value) |
218 | { |
219 | int data; |
220 | |
221 | if (regmap_read(map: regmap, reg, val: &data)) |
222 | return -EIO; |
223 | |
224 | *value = (data & bit) ? 1 : 0; |
225 | return 0; |
226 | } |
227 | |
228 | static int intel_cht_wc_pmic_update_power(struct regmap *regmap, int reg, |
229 | int bitmask, bool on) |
230 | { |
231 | return regmap_update_bits(map: regmap, reg, mask: bitmask, val: on ? 1 : 0); |
232 | } |
233 | |
234 | static int intel_cht_wc_exec_mipi_pmic_seq_element(struct regmap *regmap, |
235 | u16 i2c_client_address, |
236 | u32 reg_address, |
237 | u32 value, u32 mask) |
238 | { |
239 | u32 address; |
240 | |
241 | if (i2c_client_address > 0xff || reg_address > 0xff) { |
242 | pr_warn("%s warning addresses too big client 0x%x reg 0x%x\n" , |
243 | __func__, i2c_client_address, reg_address); |
244 | return -ERANGE; |
245 | } |
246 | |
247 | address = (i2c_client_address << 8) | reg_address; |
248 | |
249 | return regmap_update_bits(map: regmap, reg: address, mask, val: value); |
250 | } |
251 | |
252 | /* |
253 | * The thermal table and ops are empty, we do not support the Thermal opregion |
254 | * (DPTF) due to lacking documentation. |
255 | */ |
256 | static const struct intel_pmic_opregion_data intel_cht_wc_pmic_opregion_data = { |
257 | .get_power = intel_cht_wc_pmic_get_power, |
258 | .update_power = intel_cht_wc_pmic_update_power, |
259 | .exec_mipi_pmic_seq_element = intel_cht_wc_exec_mipi_pmic_seq_element, |
260 | .lpat_raw_to_temp = acpi_lpat_raw_to_temp, |
261 | .power_table = power_table, |
262 | .power_table_count = ARRAY_SIZE(power_table), |
263 | }; |
264 | |
265 | static int intel_cht_wc_pmic_opregion_probe(struct platform_device *pdev) |
266 | { |
267 | struct intel_soc_pmic *pmic = dev_get_drvdata(dev: pdev->dev.parent); |
268 | |
269 | return intel_pmic_install_opregion_handler(dev: &pdev->dev, |
270 | ACPI_HANDLE(pdev->dev.parent), |
271 | regmap: pmic->regmap, |
272 | d: &intel_cht_wc_pmic_opregion_data); |
273 | } |
274 | |
275 | static const struct platform_device_id cht_wc_opregion_id_table[] = { |
276 | { .name = "cht_wcove_region" }, |
277 | {}, |
278 | }; |
279 | |
280 | static struct platform_driver intel_cht_wc_pmic_opregion_driver = { |
281 | .probe = intel_cht_wc_pmic_opregion_probe, |
282 | .driver = { |
283 | .name = "cht_whiskey_cove_pmic" , |
284 | }, |
285 | .id_table = cht_wc_opregion_id_table, |
286 | }; |
287 | builtin_platform_driver(intel_cht_wc_pmic_opregion_driver); |
288 | |