Line data Source code
1 : /* $OpenBSD: ccpmic.c,v 1.2 2018/05/21 15:00:25 kettenis Exp $ */
2 : /*
3 : * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
4 : *
5 : * Permission to use, copy, modify, and distribute this software for any
6 : * purpose with or without fee is hereby granted, provided that the above
7 : * copyright notice and this permission notice appear in all copies.
8 : *
9 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : */
17 :
18 : #include <sys/param.h>
19 : #include <sys/systm.h>
20 : #include <sys/device.h>
21 : #include <sys/kernel.h>
22 : #include <sys/malloc.h>
23 :
24 : #include <dev/acpi/acpireg.h>
25 : #include <dev/acpi/acpivar.h>
26 : #include <dev/acpi/acpidev.h>
27 : #include <dev/acpi/amltypes.h>
28 : #include <dev/acpi/dsdt.h>
29 :
30 : #include <dev/i2c/i2cvar.h>
31 :
32 : #define CCPMIC_GPIO0P0CTLO 0x2b
33 : #define CCPMIC_GPIO0P0CTLI 0x33
34 : #define CCPMIC_GPIO1P0CTLO 0x3b
35 : #define CCPMIC_GPIO1P0CTLI 0x43
36 : #define CCPMIC_GPIOPANELCTL 0x52
37 : #define CCPMIC_GPIOCTLO_RVAL_2KUP (1 << 1)
38 : #define CCPMIC_GPIOCTLO_DRV_REN (1 << 3)
39 : #define CCPMIC_GPIOCTLO_DIR_OUT (1 << 5)
40 : #define CCPMIC_GPIOCTLI_VALUE (1 << 0)
41 :
42 : #define CCPMIC_GPIOCTLO_INPUT \
43 : (CCPMIC_GPIOCTLO_DRV_REN | CCPMIC_GPIOCTLO_RVAL_2KUP)
44 : #define CCPMIC_GPIOCTLO_OUTPUT \
45 : (CCPMIC_GPIOCTLO_INPUT | CCPMIC_GPIOCTLO_DIR_OUT)
46 :
47 : #define CCPMIC_V1P8SX 0x5d
48 : #define CCPMIC_V1P2SX 0x61
49 : #define CCPMIC_V2P85SX 0x66
50 : #define CCPMIC_PWR_ON (1 << 0)
51 : #define CCPMIC_PWR_SEL (1 << 1)
52 : #define CCPMIC_SYS2_THRM_RSLT_H 0x78
53 : #define CCPMIC_SYS2_THRM_RSLT_L 0x79
54 :
55 : #define CCPMIC_REGIONSPACE_THERMAL 0x8c
56 : #define CCPMIC_REGIONSPACE_POWER 0x8d
57 :
58 : #define CCPMIC_NPINS 16
59 :
60 : struct acpi_lpat {
61 : int32_t temp;
62 : int32_t raw;
63 : };
64 :
65 : struct ccpmic_softc {
66 : struct device sc_dev;
67 : struct acpi_softc *sc_acpi;
68 : struct aml_node *sc_node;
69 : i2c_tag_t sc_tag;
70 : i2c_addr_t sc_addr;
71 :
72 : struct acpi_lpat *sc_lpat;
73 : size_t sc_lpat_len;
74 :
75 : struct acpi_gpio sc_gpio;
76 : };
77 :
78 : int ccpmic_match(struct device *, void *, void *);
79 : void ccpmic_attach(struct device *, struct device *, void *);
80 :
81 : struct cfattach ccpmic_ca = {
82 : sizeof(struct ccpmic_softc), ccpmic_match, ccpmic_attach
83 : };
84 :
85 : struct cfdriver ccpmic_cd = {
86 : NULL, "ccpmic", DV_DULL
87 : };
88 :
89 : uint8_t ccpmic_read_1(struct ccpmic_softc *, uint8_t, int);
90 : void ccpmic_write_1(struct ccpmic_softc *, uint8_t, uint8_t, int);
91 : void ccpmic_get_lpat(struct ccpmic_softc *);
92 : int32_t ccpmic_raw_to_temp(struct ccpmic_softc *, int32_t);
93 : int ccpmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *);
94 : int ccpmic_power_opreg_handler(void *, int, uint64_t, int, uint64_t *);
95 : int ccpmic_read_pin(void *, int);
96 : void ccpmic_write_pin(void *, int, int);
97 :
98 : int
99 0 : ccpmic_match(struct device *parent, void *match, void *aux)
100 : {
101 0 : struct i2c_attach_args *ia = aux;
102 :
103 0 : return (strcmp(ia->ia_name, "INT33FD") == 0);
104 : }
105 :
106 : void
107 0 : ccpmic_attach(struct device *parent, struct device *self, void *aux)
108 : {
109 0 : struct ccpmic_softc *sc = (struct ccpmic_softc *)self;
110 0 : struct i2c_attach_args *ia = aux;
111 :
112 0 : sc->sc_tag = ia->ia_tag;
113 0 : sc->sc_addr = ia->ia_addr;
114 0 : sc->sc_acpi = acpi_softc;
115 0 : sc->sc_node = ia->ia_cookie;
116 :
117 0 : printf("\n");
118 :
119 0 : ccpmic_get_lpat(sc);
120 0 : if (sc->sc_lpat == NULL)
121 0 : return;
122 :
123 0 : sc->sc_gpio.cookie = sc;
124 0 : sc->sc_gpio.read_pin = ccpmic_read_pin;
125 0 : sc->sc_gpio.write_pin = ccpmic_write_pin;
126 0 : sc->sc_node->gpio = &sc->sc_gpio;
127 0 : acpi_register_gpio(sc->sc_acpi, sc->sc_node);
128 :
129 : /* Register OEM defined address space. */
130 0 : aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_THERMAL,
131 : sc, ccpmic_thermal_opreg_handler);
132 0 : aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_POWER,
133 : sc, ccpmic_power_opreg_handler);
134 0 : }
135 :
136 : uint8_t
137 0 : ccpmic_read_1(struct ccpmic_softc *sc, uint8_t reg, int flags)
138 : {
139 0 : uint8_t val;
140 : int error;
141 :
142 0 : iic_acquire_bus(sc->sc_tag, flags);
143 0 : error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
144 : ®, sizeof(reg), &val, sizeof(val), flags);
145 0 : iic_release_bus(sc->sc_tag, flags);
146 :
147 0 : if (error) {
148 0 : printf("%s: can't read register 0x%02x\n",
149 0 : sc->sc_dev.dv_xname, reg);
150 0 : val = 0xff;
151 0 : }
152 :
153 0 : return val;
154 0 : }
155 :
156 : void
157 0 : ccpmic_write_1(struct ccpmic_softc *sc, uint8_t reg, uint8_t val, int flags)
158 : {
159 : int error;
160 :
161 0 : iic_acquire_bus(sc->sc_tag, flags);
162 0 : error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
163 : ®, sizeof(reg), &val, sizeof(val), flags);
164 0 : iic_release_bus(sc->sc_tag, flags);
165 :
166 0 : if (error) {
167 0 : printf("%s: can't write register 0x%02x\n",
168 0 : sc->sc_dev.dv_xname, reg);
169 0 : }
170 0 : }
171 :
172 : void
173 0 : ccpmic_get_lpat(struct ccpmic_softc *sc)
174 : {
175 0 : struct aml_value res;
176 : int i;
177 :
178 0 : if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res))
179 0 : return;
180 0 : if (res.type != AML_OBJTYPE_PACKAGE)
181 : goto out;
182 0 : if (res.length < 4 || (res.length % 2) != 0)
183 : goto out;
184 :
185 0 : sc->sc_lpat_len = res.length / 2;
186 0 : sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat),
187 : M_DEVBUF, M_WAITOK);
188 :
189 0 : for (i = 0; i < sc->sc_lpat_len; i++) {
190 0 : sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]);
191 0 : sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]);
192 : }
193 :
194 : out:
195 0 : aml_freevalue(&res);
196 0 : }
197 :
198 : int32_t
199 0 : ccpmic_raw_to_temp(struct ccpmic_softc *sc, int32_t raw)
200 : {
201 0 : struct acpi_lpat *lpat = sc->sc_lpat;
202 : int32_t raw0, delta_raw;
203 : int32_t temp0, delta_temp;
204 : int i;
205 :
206 0 : for (i = 1; i < sc->sc_lpat_len; i++) {
207 : /* Coefficient can be positive or negative. */
208 0 : if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw)
209 : break;
210 0 : if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw)
211 : break;
212 : }
213 0 : if (i == sc->sc_lpat_len)
214 0 : return -1;
215 :
216 0 : raw0 = lpat[i - 1].raw;
217 0 : temp0 = lpat[i - 1].temp;
218 0 : delta_raw = lpat[i].raw - raw0;
219 0 : delta_temp = lpat[i].temp - temp0;
220 :
221 0 : return temp0 + (raw - raw0) * delta_temp / delta_raw;
222 0 : }
223 :
224 : struct ccpmic_regmap {
225 : uint8_t address;
226 : uint8_t hi, lo;
227 : };
228 :
229 : struct ccpmic_regmap ccpmic_thermal_regmap[] = {
230 : { 0x18, CCPMIC_SYS2_THRM_RSLT_H, CCPMIC_SYS2_THRM_RSLT_L }, /* TMP2 */
231 : };
232 :
233 : int
234 0 : ccpmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address,
235 : int size, uint64_t *value)
236 : {
237 0 : struct ccpmic_softc *sc = cookie;
238 : int32_t temp;
239 : uint16_t raw;
240 : uint8_t lo, hi;
241 : int i;
242 :
243 : /* Only allow 32-bit read access. */
244 0 : if (size != 4 || iodir != ACPI_IOREAD)
245 0 : return -1;
246 :
247 0 : for (i = 0; i < nitems(ccpmic_thermal_regmap); i++) {
248 0 : if (address == ccpmic_thermal_regmap[i].address)
249 : break;
250 : }
251 0 : if (i == nitems(ccpmic_thermal_regmap)) {
252 0 : printf("%s: addr 0x%02llx\n", __func__, address);
253 0 : return -1;
254 : }
255 :
256 0 : lo = ccpmic_thermal_regmap[i].lo;
257 0 : hi = ccpmic_thermal_regmap[i].hi;
258 0 : raw = ccpmic_read_1(sc, lo, 0);
259 0 : raw |= (ccpmic_read_1(sc, hi, 0) & 0x03) << 8;
260 :
261 0 : temp = ccpmic_raw_to_temp(sc, raw);
262 0 : if (temp < 0)
263 0 : return -1;
264 :
265 0 : *value = temp;
266 0 : return 0;
267 0 : }
268 :
269 : struct ccpmic_regmap ccpmic_power_regmap[] = {
270 : { 0x24, CCPMIC_V2P85SX }, /* X285 */
271 : { 0x48, CCPMIC_V1P8SX }, /* V18X */
272 : { 0x50, CCPMIC_V1P2SX }, /* V12X */
273 : };
274 :
275 : int
276 0 : ccpmic_power_opreg_handler(void *cookie, int iodir, uint64_t address,
277 : int size, uint64_t *value)
278 : {
279 0 : struct ccpmic_softc *sc = cookie;
280 : uint8_t reg, val;
281 : int i;
282 :
283 : /* Only allow 32-bit access. */
284 0 : if (size != 4)
285 0 : return -1;
286 :
287 0 : for (i = 0; i < nitems(ccpmic_power_regmap); i++) {
288 0 : if (address == ccpmic_power_regmap[i].address)
289 : break;
290 : }
291 0 : if (i == nitems(ccpmic_power_regmap)) {
292 0 : printf("%s: addr 0x%02llx\n", __func__, address);
293 0 : return -1;
294 : }
295 :
296 0 : reg = ccpmic_power_regmap[i].hi;
297 0 : val = ccpmic_read_1(sc, reg, 0);
298 0 : if (iodir == ACPI_IOREAD) {
299 0 : if ((val & CCPMIC_PWR_SEL) && (val & CCPMIC_PWR_ON))
300 0 : *value = 1;
301 : else
302 0 : *value = 0;
303 : } else {
304 0 : if (*value)
305 0 : val |= CCPMIC_PWR_ON;
306 : else
307 0 : val &= ~CCPMIC_PWR_ON;
308 0 : ccpmic_write_1(sc, reg, val | CCPMIC_PWR_SEL, 0);
309 : }
310 :
311 0 : return 0;
312 0 : }
313 :
314 : /*
315 : * We have 16 real GPIOs and a bunch of virtual ones. The virtual
316 : * ones are mostly there to deal with a limitation of Microsoft
317 : * Windows. We only implement the "panel" control GPIO, which
318 : * actually maps onto a real GPIO.
319 : */
320 :
321 : int
322 0 : ccpmic_read_pin(void *cookie, int pin)
323 : {
324 0 : struct ccpmic_softc *sc = cookie;
325 : uint8_t reg;
326 :
327 0 : if (pin >= CCPMIC_NPINS)
328 0 : return 0;
329 :
330 0 : reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
331 0 : ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_INPUT, 0);
332 0 : reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLI : CCPMIC_GPIO1P0CTLI) + pin % 8;
333 0 : return ccpmic_read_1(sc, reg, 0) & CCPMIC_GPIOCTLI_VALUE;
334 0 : }
335 :
336 : void
337 0 : ccpmic_write_pin(void *cookie, int pin, int value)
338 : {
339 0 : struct ccpmic_softc *sc = cookie;
340 : uint8_t reg;
341 :
342 0 : if (pin == 0x5e) {
343 : reg = CCPMIC_GPIOPANELCTL;
344 0 : ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
345 0 : return;
346 : }
347 :
348 0 : if (pin >= CCPMIC_NPINS)
349 0 : return;
350 :
351 0 : reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
352 0 : ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
353 0 : }
|