xref: /openbsd/sys/dev/pci/amdiic.c (revision 3bef86f7)
1 /*	$OpenBSD: amdiic.c,v 1.13 2022/03/11 18:00:45 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org>
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 /*
20  * AMD-8111 SMBus controller driver.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/kernel.h>
27 #include <sys/rwlock.h>
28 
29 #include <machine/bus.h>
30 
31 #include <dev/pci/pcidevs.h>
32 #include <dev/pci/pcireg.h>
33 #include <dev/pci/pcivar.h>
34 
35 #include <dev/i2c/i2cvar.h>
36 
37 #ifdef AMDIIC_DEBUG
38 #define DPRINTF(x) printf x
39 #else
40 #define DPRINTF(x)
41 #endif
42 
43 #define AMDIIC_DELAY	100
44 #define AMDIIC_TIMEOUT	1
45 
46 /* PCI configuration registers */
47 #define AMD8111_SMB_BASE	0x10	/* SMBus base address */
48 #define AMD8111_SMB_MISC	0x48	/* miscellaneous control */
49 #define AMD8111_SMB_MISC_SU	(1 << 0)	/* 16x clock speed-up */
50 #define AMD8111_SMB_MISC_INTEN	(1 << 1)	/* PCI IRQ enabled */
51 #define AMD8111_SMB_MISC_SCIEN	(1 << 2)	/* SCI enabled */
52 
53 /* SMBus I/O registers */
54 #define AMD8111_SMB_SC_DATA	0x00	/* data port */
55 #define AMD8111_SMB_SC_ST	0x04	/* status */
56 #define AMD8111_SMB_SC_ST_OBF	(1 << 0)	/* output buffer full */
57 #define AMD8111_SMB_SC_ST_IBF	(1 << 1)	/* input buffer full */
58 #define AMD8111_SMB_SC_ST_CMD	(1 << 3)	/* command byte */
59 #define AMD8111_SMB_SC_ST_BITS	"\020\001OBF\002IBF\004CMD"
60 #define AMD8111_SMB_SC_CMD	0x04	/* command port */
61 #define AMD8111_SMB_SC_CMD_RD	0x80		/* read */
62 #define AMD8111_SMB_SC_CMD_WR	0x81		/* write */
63 #define AMD8111_SMB_SC_IC	0x08	/* interrupt control */
64 
65 /* Host controller interface registers */
66 #define AMD8111_SMB_PROTO	0x00	/* protocol */
67 #define AMD8111_SMB_PROTO_READ	0x01		/* read direction */
68 #define AMD8111_SMB_PROTO_QUICK	0x02		/* QUICK command */
69 #define AMD8111_SMB_PROTO_BYTE	0x04		/* BYTE command */
70 #define AMD8111_SMB_PROTO_BDATA	0x06		/* BYTE DATA command */
71 #define AMD8111_SMB_PROTO_WDATA	0x08		/* WORD DATA command */
72 #define AMD8111_SMB_STAT	0x01	/* status */
73 #define AMD8111_SMB_STAT_MASK	0x1f
74 #define AMD8111_SMB_STAT_DONE	(1 << 7)	/* command completion */
75 #define AMD8111_SMB_ADDR	0x02	/* address */
76 #define AMD8111_SMB_ADDR_SHIFT	1
77 #define AMD8111_SMB_CMD		0x03	/* SMBus command */
78 #define AMD8111_SMB_DATA(x)	(0x04 + (x)) /* SMBus data */
79 
80 struct amdiic_softc {
81 	struct device		sc_dev;
82 
83 	bus_space_tag_t		sc_iot;
84 	bus_space_handle_t	sc_ioh;
85 	void *			sc_ih;
86 	int			sc_poll;
87 
88 	struct i2c_controller	sc_i2c_tag;
89 	struct rwlock		sc_i2c_lock;
90 	struct {
91 		i2c_op_t     op;
92 		void *       buf;
93 		size_t       len;
94 		int          flags;
95 		volatile int error;
96 	}			sc_i2c_xfer;
97 };
98 
99 int	amdiic_match(struct device *, void *, void *);
100 void	amdiic_attach(struct device *, struct device *, void *);
101 
102 int	amdiic_read(struct amdiic_softc *, u_int8_t);
103 int	amdiic_write(struct amdiic_softc *, u_int8_t, u_int8_t);
104 int	amdiic_wait(struct amdiic_softc *, int);
105 
106 int	amdiic_i2c_acquire_bus(void *, int);
107 void	amdiic_i2c_release_bus(void *, int);
108 int	amdiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
109 	    void *, size_t, int);
110 
111 int	amdiic_intr(void *);
112 
113 const struct cfattach amdiic_ca = {
114 	sizeof(struct amdiic_softc),
115 	amdiic_match,
116 	amdiic_attach
117 };
118 
119 struct cfdriver amdiic_cd = {
120 	NULL, "amdiic", DV_DULL
121 };
122 
123 const struct pci_matchid amdiic_ids[] = {
124 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_SMB }
125 };
126 
127 int
128 amdiic_match(struct device *parent, void *match, void *aux)
129 {
130 	return (pci_matchbyid(aux, amdiic_ids,
131 	    sizeof(amdiic_ids) / sizeof(amdiic_ids[0])));
132 }
133 
134 void
135 amdiic_attach(struct device *parent, struct device *self, void *aux)
136 {
137 	struct amdiic_softc *sc = (struct amdiic_softc *)self;
138 	struct pci_attach_args *pa = aux;
139 	struct i2cbus_attach_args iba;
140 	pcireg_t conf;
141 	bus_size_t iosize;
142 	pci_intr_handle_t ih;
143 	const char *intrstr = NULL;
144 
145 	/* Map I/O space */
146 	if (pci_mapreg_map(pa, AMD8111_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
147 	    &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
148 		printf(": can't map i/o space\n");
149 		return;
150 	}
151 
152 	/* Read configuration */
153 	conf = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_SMB_MISC);
154 	DPRINTF((": conf 0x%08x", conf));
155 
156 	sc->sc_poll = 1;
157 	if (conf & AMD8111_SMB_MISC_SCIEN) {
158 		/* No PCI IRQ */
159 		printf(": SCI");
160 	} else if (conf & AMD8111_SMB_MISC_INTEN) {
161 		/* Install interrupt handler */
162 		if (pci_intr_map(pa, &ih) == 0) {
163 			intrstr = pci_intr_string(pa->pa_pc, ih);
164 			sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
165 			    amdiic_intr, sc, sc->sc_dev.dv_xname);
166 			if (sc->sc_ih != NULL) {
167 				printf(": %s", intrstr);
168 				sc->sc_poll = 0;
169 			}
170 		}
171 		if (sc->sc_poll)
172 			printf(": polling");
173 	}
174 
175 	printf("\n");
176 
177 	/* Attach I2C bus */
178 	rw_init(&sc->sc_i2c_lock, "iiclk");
179 	sc->sc_i2c_tag.ic_cookie = sc;
180 	sc->sc_i2c_tag.ic_acquire_bus = amdiic_i2c_acquire_bus;
181 	sc->sc_i2c_tag.ic_release_bus = amdiic_i2c_release_bus;
182 	sc->sc_i2c_tag.ic_exec = amdiic_i2c_exec;
183 
184 	bzero(&iba, sizeof(iba));
185 	iba.iba_name = "iic";
186 	iba.iba_tag = &sc->sc_i2c_tag;
187 	config_found(self, &iba, iicbus_print);
188 
189 	return;
190 }
191 
192 int
193 amdiic_read(struct amdiic_softc *sc, u_int8_t reg)
194 {
195 	if (amdiic_wait(sc, 0))
196 		return (-1);
197 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
198 	    AMD8111_SMB_SC_CMD_RD);
199 	if (amdiic_wait(sc, 0))
200 		return (-1);
201 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
202 	if (amdiic_wait(sc, 1))
203 		return (-1);
204 
205 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA));
206 }
207 
208 int
209 amdiic_write(struct amdiic_softc *sc, u_int8_t reg, u_int8_t val)
210 {
211 	if (amdiic_wait(sc, 0))
212 		return (-1);
213 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
214 	    AMD8111_SMB_SC_CMD_WR);
215 	if (amdiic_wait(sc, 0))
216 		return (-1);
217 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
218 	if (amdiic_wait(sc, 0))
219 		return (-1);
220 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, val);
221 
222 	return (0);
223 }
224 
225 int
226 amdiic_wait(struct amdiic_softc *sc, int output)
227 {
228 	int retries;
229 	u_int8_t st;
230 
231 	for (retries = 100; retries > 0; retries--) {
232 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
233 		    AMD8111_SMB_SC_ST);
234 		if (output && (st & AMD8111_SMB_SC_ST_OBF))
235 			return (0);
236 		if (!output && (st & AMD8111_SMB_SC_ST_IBF) == 0)
237 			return (0);
238 		DELAY(1);
239 	}
240 	DPRINTF(("%s: %s wait timeout: st 0x%b\n", sc->sc_dev.dv_xname,
241 	    (output ? "output" : "input"), st));
242 
243 	return (1);
244 }
245 
246 int
247 amdiic_i2c_acquire_bus(void *cookie, int flags)
248 {
249 	struct amdiic_softc *sc = cookie;
250 
251 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
252 		return (0);
253 
254 	return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
255 }
256 
257 void
258 amdiic_i2c_release_bus(void *cookie, int flags)
259 {
260 	struct amdiic_softc *sc = cookie;
261 
262 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
263 		return;
264 
265 	rw_exit(&sc->sc_i2c_lock);
266 }
267 
268 int
269 amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
270     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
271 {
272 	struct amdiic_softc *sc = cookie;
273 	u_int8_t *b;
274 	u_int8_t proto, st;
275 	int retries;
276 
277 	DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
278 	    "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
279 	    cmdlen, len, flags));
280 
281 	if (cold || sc->sc_poll)
282 		flags |= I2C_F_POLL;
283 
284 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
285 		return (1);
286 
287 	/* Setup transfer */
288 	sc->sc_i2c_xfer.op = op;
289 	sc->sc_i2c_xfer.buf = buf;
290 	sc->sc_i2c_xfer.len = len;
291 	sc->sc_i2c_xfer.flags = flags;
292 	sc->sc_i2c_xfer.error = 0;
293 
294 	/* Set slave address */
295 	if (amdiic_write(sc, AMD8111_SMB_ADDR,
296 	    addr << AMD8111_SMB_ADDR_SHIFT) == -1)
297 		return (1);
298 
299 	b = (void *)cmdbuf;
300 	if (cmdlen > 0)
301 		/* Set command byte */
302 		if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
303 			return (1);
304 
305 	if (I2C_OP_WRITE_P(op)) {
306 		/* Write data */
307 		b = buf;
308 		if (len > 0)
309 			if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
310 				return (1);
311 		if (len > 1)
312 			if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
313 				return (1);
314 	}
315 
316 	/* Set SMBus command */
317 	if (len == 0)
318 		proto = AMD8111_SMB_PROTO_BYTE;
319 	else if (len == 1)
320 		proto = AMD8111_SMB_PROTO_BDATA;
321 	else if (len == 2)
322 		proto = AMD8111_SMB_PROTO_WDATA;
323 	else
324 		panic("%s: unexpected len %zd", __func__, len);
325 
326 	/* Set direction */
327 	if (I2C_OP_READ_P(op))
328 		proto |= AMD8111_SMB_PROTO_READ;
329 
330 	/* Start transaction */
331 	amdiic_write(sc, AMD8111_SMB_PROTO, proto);
332 
333 	if (flags & I2C_F_POLL) {
334 		/* Poll for completion */
335 		DELAY(AMDIIC_DELAY);
336 		for (retries = 1000; retries > 0; retries--) {
337 			st = amdiic_read(sc, AMD8111_SMB_STAT);
338 			if (st != 0)
339 				break;
340 			DELAY(AMDIIC_DELAY);
341 		}
342 		if (st == 0) {
343 			printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, "
344 			    "len %zu, flags 0x%02x: timeout\n",
345 			    sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
346 			return (1);
347 		}
348 		amdiic_intr(sc);
349 	} else {
350 		/* Wait for interrupt */
351 		if (tsleep_nsec(sc, PRIBIO, "amdiic",
352 		    SEC_TO_NSEC(AMDIIC_TIMEOUT)))
353 			return (1);
354 	}
355 
356 	if (sc->sc_i2c_xfer.error)
357 		return (1);
358 
359 	return (0);
360 }
361 
362 int
363 amdiic_intr(void *arg)
364 {
365 	struct amdiic_softc *sc = arg;
366 	int st;
367 	u_int8_t *b;
368 	size_t len;
369 
370 	/* Read status */
371 	if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
372 		return (-1);
373 	if (st == 0)
374 		/* Interrupt was not for us */
375 		return (0);
376 
377 	DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));
378 
379 	/* Check for errors */
380 	if ((st & AMD8111_SMB_STAT_MASK) != 0) {
381 		sc->sc_i2c_xfer.error = 1;
382 		goto done;
383 	}
384 
385 	if (st & AMD8111_SMB_STAT_DONE) {
386 		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
387 			goto done;
388 
389 		/* Read data */
390 		b = sc->sc_i2c_xfer.buf;
391 		len = sc->sc_i2c_xfer.len;
392 		if (len > 0)
393 			b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
394 		if (len > 1)
395 			b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
396 	}
397 
398 done:
399 	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
400 		wakeup(sc);
401 	return (1);
402 }
403