xref: /openbsd/sys/dev/pci/alipm.c (revision 4bdff4be)
1 /*	$OpenBSD: alipm.c,v 1.17 2022/03/11 18:00:45 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Mark Kettenis
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/kernel.h>
22 #include <sys/rwlock.h>
23 #include <sys/systm.h>
24 
25 #include <dev/i2c/i2cvar.h>
26 
27 #include <dev/pci/pcidevs.h>
28 #include <dev/pci/pcireg.h>
29 #include <dev/pci/pcivar.h>
30 
31 #ifdef __sparc64__
32 #include <arch/sparc64/dev/ofwi2cvar.h>
33 #endif
34 
35 /*
36  * Acer Labs M7101 Power register definitions.
37  */
38 
39 /* PCI configuration registers. */
40 #define ALIPM_CONF	0xd0		/* general configuration */
41 #define ALIPM_CONF_SMBEN	0x0400		/* enable SMBus */
42 #define ALIPM_BASE	0xe0		/* ACPI and SMBus base address */
43 #define ALIPM_SMB_HOSTC	0xf0		/* host configuration */
44 #define ALIPM_SMB_HOSTC_HSTEN	0x00000001	/* enable host controller */
45 #define ALIPM_SMB_HOSTC_CLOCK	0x00e00000	/* clock speed */
46 #define ALIPM_SMB_HOSTC_149K	0x00000000	/* 149 KHz clock */
47 #define ALIPM_SMB_HOSTC_74K	0x00200000	/*  74 KHz clock */
48 #define ALIPM_SMB_HOSTC_37K	0x00400000	/*  37 KHz clock */
49 #define ALIPM_SMB_HOSTC_223K	0x00800000	/* 223 KHz clock */
50 #define ALIPM_SMB_HOSTC_111K	0x00a00000	/* 111 KHz clock */
51 #define ALIPM_SMB_HOSTC_55K	0x00c00000	/*  55 KHz clock */
52 
53 #define ALIPM_SMB_SIZE		32	/* SMBus I/O space size */
54 
55 /* SMBus I/O registers */
56 #define ALIPM_SMB_HS	0x00		/* host status */
57 #define ALIPM_SMB_HS_IDLE	0x04
58 #define ALIPM_SMB_HS_BUSY	0x08	/* running a command */
59 #define ALIPM_SMB_HS_DONE	0x10	/* command completed */
60 #define ALIPM_SMB_HS_DEVERR	0x20	/* command error */
61 #define ALIPM_SMB_HS_BUSERR	0x40	/* transaction collision */
62 #define ALIPM_SMB_HS_FAILED	0x80	/* failed bus transaction */
63 #define ALIPM_SMB_HS_BITS \
64   "\020\003IDLE\004BUSY\005DONE\006DEVERR\007BUSERR\010FAILED"
65 #define ALIPM_SMB_HC	0x01		/* host control */
66 #define ALIPM_SMB_HC_KILL	0x04		/* kill command */
67 #define ALIPM_SMB_HC_RESET	0x08		/* reset bus */
68 #define ALIPM_SMB_HC_CMD_QUICK	0x00		/* QUICK command */
69 #define ALIPM_SMB_HC_CMD_BYTE	0x10		/* BYTE command */
70 #define ALIPM_SMB_HC_CMD_BDATA	0x20		/* BYTE DATA command */
71 #define ALIPM_SMB_HC_CMD_WDATA	0x30		/* WORD DATA command */
72 #define ALIPM_SMB_HC_CMD_BLOCK 0x40		/* BLOCK command */
73 #define ALIPM_SMB_START		0x02	/* start command */
74 #define ALIPM_SMB_TXSLVA	0x03	/* transmit slave address */
75 #define ALIPM_SMB_TXSLVA_READ	(1 << 0)	/* read direction */
76 #define ALIPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
77 #define ALIPM_SMB_HD0		0x04	/* host data 0 */
78 #define ALIPM_SMB_HD1		0x05	/* host data 1 */
79 #define ALIPM_SMB_HBDB		0x06	/* host block data byte */
80 #define ALIPM_SMB_HCMD		0x07	/* host command */
81 
82 /*
83  * Newer chips have a more standard, but different PCI configuration
84  * register layout.
85  */
86 
87 #define ALIPM_SMB_BASE	0x14		/* SMBus base address */
88 #define ALIPM_SMB_HOSTX	0xe0		/* host configuration */
89 
90 #ifdef ALIPM_DEBUG
91 #define DPRINTF(x) printf x
92 #else
93 #define DPRINTF(x)
94 #endif
95 
96 #define ALIPM_DELAY	100
97 #define ALIPM_TIMEOUT	1
98 
99 struct alipm_softc {
100 	struct device sc_dev;
101 
102 	bus_space_tag_t sc_iot;
103 	bus_space_handle_t sc_ioh;
104 
105 	struct i2c_controller sc_smb_tag;
106 	struct rwlock sc_smb_lock;
107 };
108 
109 int	alipm_match(struct device *, void *, void *);
110 void	alipm_attach(struct device *, struct device *, void *);
111 
112 int	alipm_smb_acquire_bus(void *, int);
113 void	alipm_smb_release_bus(void *, int);
114 int	alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
115 	    size_t, void *, size_t, int);
116 
117 const struct cfattach alipm_ca = {
118 	sizeof(struct alipm_softc),
119 	alipm_match,
120 	alipm_attach
121 };
122 
123 struct cfdriver alipm_cd = {
124 	NULL, "alipm", DV_DULL
125 };
126 
127 int
128 alipm_match(struct device *parent, void *match, void *aux)
129 {
130 	struct pci_attach_args *pa = aux;
131 
132 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
133 	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M7101))
134 		return (1);
135 	return (0);
136 }
137 
138 void
139 alipm_attach(struct device *parent, struct device *self, void *aux)
140 {
141 	struct alipm_softc *sc = (struct alipm_softc *) self;
142 	struct pci_attach_args *pa = aux;
143 	struct i2cbus_attach_args iba;
144 	pcireg_t iobase, reg;
145 	bus_size_t iosize = ALIPM_SMB_SIZE;
146 
147 	/* Old chips don't have the PCI 2.2 Capabilities List. */
148 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
149 	if ((reg & PCI_STATUS_CAPLIST_SUPPORT) == 0) {
150 		/* Map I/O space */
151 		iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_BASE);
152 		sc->sc_iot = pa->pa_iot;
153 		if (iobase == 0 ||
154 		    bus_space_map(sc->sc_iot, iobase >> 16,
155 		    iosize, 0, &sc->sc_ioh)) {
156 			printf(": can't map i/o space\n");
157 			return;
158 		}
159 
160 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_CONF);
161 		if ((reg & ALIPM_CONF_SMBEN) == 0) {
162 			printf(": SMBus disabled\n");
163 			goto fail;
164 		}
165 
166 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTC);
167 		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
168 			printf(": SMBus host disabled\n");
169 			goto fail;
170 		}
171 	} else {
172 		/* Map I/O space */
173 		if (pci_mapreg_map(pa, ALIPM_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
174 		    &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, ALIPM_SMB_SIZE)) {
175 			printf(": can't map i/o space\n");
176 			return;
177 		}
178 
179 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTX);
180 		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
181 			printf(": SMBus host disabled\n");
182 			goto fail;
183 		}
184 	}
185 
186 	switch (reg & ALIPM_SMB_HOSTC_CLOCK) {
187 	case ALIPM_SMB_HOSTC_149K:
188 		printf(": 149KHz clock");
189 		break;
190 	case ALIPM_SMB_HOSTC_74K:
191 		printf(": 74KHz clock");
192 		break;
193 	case ALIPM_SMB_HOSTC_37K:
194 		printf(": 37KHz clock");
195 		break;
196 	case ALIPM_SMB_HOSTC_223K:
197 		printf(": 223KHz clock");
198 		break;
199 	case ALIPM_SMB_HOSTC_111K:
200 		printf(": 111KHz clock");
201 		break;
202 	case ALIPM_SMB_HOSTC_55K:
203 		printf(": 55KHz clock");
204 		break;
205 	default:
206 		printf(" unknown clock speed");
207 		break;
208 	}
209 
210 	printf("\n");
211 
212 	/* Attach I2C bus */
213 	rw_init(&sc->sc_smb_lock, "alipm");
214 	sc->sc_smb_tag.ic_cookie = sc;
215 	sc->sc_smb_tag.ic_acquire_bus = alipm_smb_acquire_bus;
216 	sc->sc_smb_tag.ic_release_bus = alipm_smb_release_bus;
217 	sc->sc_smb_tag.ic_exec = alipm_smb_exec;
218 
219 	bzero(&iba, sizeof iba);
220 	iba.iba_name = "iic";
221 	iba.iba_tag = &sc->sc_smb_tag;
222 #ifdef __sparc64__
223 	iba.iba_bus_scan = ofwiic_pci_scan;
224 	iba.iba_bus_scan_arg = pa;
225 #endif
226 	config_found(&sc->sc_dev, &iba, iicbus_print);
227 
228 	return;
229 
230 fail:
231 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
232 }
233 
234 int
235 alipm_smb_acquire_bus(void *cookie, int flags)
236 {
237 	struct alipm_softc *sc = cookie;
238 
239 	if (flags & I2C_F_POLL)
240 		return (0);
241 
242 	return (rw_enter(&sc->sc_smb_lock, RW_WRITE | RW_INTR));
243 }
244 
245 void
246 alipm_smb_release_bus(void *cookie, int flags)
247 {
248 	struct alipm_softc *sc = cookie;
249 
250 	if (flags & I2C_F_POLL)
251 		return;
252 
253 	rw_exit(&sc->sc_smb_lock);
254 }
255 
256 int
257 alipm_smb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
258     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
259 {
260 	struct alipm_softc *sc = cookie;
261 	u_int8_t *b;
262 	u_int8_t ctl, st;
263 	int retries, error = 0;
264 
265 	DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
266 	    "flags 0x%x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
267 	    len, flags));
268 
269 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
270 		return (EOPNOTSUPP);
271 
272 	/* Clear status bits */
273 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS,
274 	    ALIPM_SMB_HS_DONE | ALIPM_SMB_HS_FAILED |
275 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR);
276 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
277 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
278 
279 	/* Wait until bus is idle */
280 	for (retries = 1000; retries > 0; retries--) {
281 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
282 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
283 		    BUS_SPACE_BARRIER_READ);
284 		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
285 		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
286 			break;
287 		DELAY(ALIPM_DELAY);
288 	}
289 	if (retries == 0) {
290 		printf("%s: timeout st 0x%b\n", sc->sc_dev.dv_xname,
291 		    st, ALIPM_SMB_HS_BITS);
292 		return (ETIMEDOUT);
293 	}
294 	if (st & (ALIPM_SMB_HS_FAILED |
295 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
296 		printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
297 		    st, ALIPM_SMB_HS_BITS);
298 		return (EIO);
299 	}
300 
301 	/* Set slave address and transfer direction. */
302 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_TXSLVA,
303 	    ALIPM_SMB_TXSLVA_ADDR(addr) |
304 	    (I2C_OP_READ_P(op) ? ALIPM_SMB_TXSLVA_READ : 0));
305 
306 	b = (void *)cmdbuf;
307 	if (cmdlen > 0)
308 		/* Set command byte */
309 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
310 		     ALIPM_SMB_HCMD, b[0]);
311 
312 	if (I2C_OP_WRITE_P(op)) {
313 		/* Write data. */
314 		b = buf;
315 		if (len > 0)
316 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
317 			    ALIPM_SMB_HD0, b[0]);
318 		if (len > 1)
319 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
320 			    ALIPM_SMB_HD1, b[1]);
321 	}
322 
323 	/* Set SMBus command */
324 	if (len == 0)
325 		ctl = ALIPM_SMB_HC_CMD_BYTE;
326 	else if (len == 1)
327 		ctl = ALIPM_SMB_HC_CMD_BDATA;
328 	else if (len == 2)
329 		ctl = ALIPM_SMB_HC_CMD_WDATA;
330 	else
331 		panic("%s: unexpected len %zd", __func__, len);
332 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC, ctl);
333 
334 	/* Start transaction */
335 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
336 	    BUS_SPACE_BARRIER_WRITE);
337 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_START, 0xff);
338 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
339 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
340 
341 	/* Poll for completion */
342 	DELAY(ALIPM_DELAY);
343 	for (retries = 1000; retries > 0; retries--) {
344 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
345 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
346 		    BUS_SPACE_BARRIER_READ);
347 		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
348 		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
349 			break;
350 		DELAY(ALIPM_DELAY);
351 	}
352 	if (retries == 0) {
353 		printf("%s: timeout st 0x%b, resetting\n",
354 		    sc->sc_dev.dv_xname, st, ALIPM_SMB_HS_BITS);
355 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
356 		    ALIPM_SMB_HC_RESET);
357 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
358 		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
359 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
360 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
361 		    BUS_SPACE_BARRIER_READ);
362 		error = ETIMEDOUT;
363 		goto done;
364 	}
365 
366 	if ((st & ALIPM_SMB_HS_DONE) == 0) {
367 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
368 		     ALIPM_SMB_HC_KILL);
369 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
370 		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
371 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
372 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
373 		    BUS_SPACE_BARRIER_READ);
374 		if ((st & ALIPM_SMB_HS_FAILED) == 0)
375 			printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
376 			    st, ALIPM_SMB_HS_BITS);
377 	}
378 
379 	/* Check for errors */
380 	if (st & (ALIPM_SMB_HS_FAILED |
381 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
382 		error = EIO;
383 		goto done;
384 	}
385 
386 	if (I2C_OP_READ_P(op)) {
387 		/* Read data */
388 		b = buf;
389 		if (len > 0) {
390 			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
391 			    ALIPM_SMB_HD0);
392 			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
393 			    ALIPM_SMB_HD0, 1, BUS_SPACE_BARRIER_READ);
394 		}
395 		if (len > 1) {
396 			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
397 			    ALIPM_SMB_HD1);
398 			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
399 			    ALIPM_SMB_HD1, 1, BUS_SPACE_BARRIER_READ);
400 		}
401 	}
402 
403 done:
404 	/* Clear status bits */
405 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, st);
406 
407 	return (error);
408 }
409