Annotation of src/sys/dev/i2c/i2c.c, Revision 1.19.4.3
1.19.4.3! yamt 1: /* $NetBSD: i2c.c,v 1.19.4.2 2009/05/04 08:12:39 yamt Exp $ */
1.1 thorpej 2:
3: /*
4: * Copyright (c) 2003 Wasabi Systems, Inc.
5: * All rights reserved.
6: *
7: * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed for the NetBSD Project by
20: * Wasabi Systems, Inc.
21: * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22: * or promote products derived from this software without specific prior
23: * written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35: * POSSIBILITY OF SUCH DAMAGE.
36: */
37:
1.18 lukem 38: #include <sys/cdefs.h>
1.19.4.3! yamt 39: __KERNEL_RCSID(0, "$NetBSD: i2c.c,v 1.19.4.2 2009/05/04 08:12:39 yamt Exp $");
1.18 lukem 40:
1.1 thorpej 41: #include <sys/param.h>
42: #include <sys/systm.h>
43: #include <sys/device.h>
44: #include <sys/event.h>
45: #include <sys/conf.h>
1.11 jmcneill 46: #include <sys/malloc.h>
47: #include <sys/kthread.h>
48: #include <sys/proc.h>
49: #include <sys/kernel.h>
1.1 thorpej 50:
51: #include <dev/i2c/i2cvar.h>
52:
53: #include "locators.h"
1.19.4.2 yamt 54: #include <opt_i2cbus.h>
1.1 thorpej 55:
56: struct iic_softc {
57: i2c_tag_t sc_tag;
1.6 jmcneill 58: int sc_type;
1.1 thorpej 59: };
60:
1.11 jmcneill 61: static void iic_smbus_intr_thread(void *);
1.19.4.3! yamt 62: static void iic_fill_compat(struct i2c_attach_args*, const char*,
! 63: size_t, char **);
1.11 jmcneill 64:
1.1 thorpej 65: int
1.10 christos 66: iicbus_print(void *aux, const char *pnp)
1.1 thorpej 67: {
68:
69: if (pnp != NULL)
1.8 drochner 70: aprint_normal("iic at %s", pnp);
1.1 thorpej 71:
72: return (UNCONF);
73: }
74:
75: static int
1.19.4.3! yamt 76: iic_print_direct(void *aux, const char *pnp)
! 77: {
! 78: struct i2c_attach_args *ia = aux;
! 79:
! 80: if (pnp != NULL)
! 81: aprint_normal("%s at %s addr 0x%02x", ia->ia_name, pnp,
! 82: ia->ia_addr);
! 83: else
! 84: aprint_normal(" addr 0x%02x", ia->ia_addr);
! 85:
! 86: return UNCONF;
! 87: }
! 88:
! 89: static int
1.10 christos 90: iic_print(void *aux, const char *pnp)
1.1 thorpej 91: {
92: struct i2c_attach_args *ia = aux;
93:
1.6 jmcneill 94: if (ia->ia_addr != (i2c_addr_t)-1)
95: aprint_normal(" addr 0x%x", ia->ia_addr);
1.1 thorpej 96:
97: return (UNCONF);
98: }
99:
100: static int
1.19.4.1 yamt 101: iic_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
1.1 thorpej 102: {
1.19.4.1 yamt 103: struct iic_softc *sc = device_private(parent);
1.1 thorpej 104: struct i2c_attach_args ia;
105:
106: ia.ia_tag = sc->sc_tag;
107: ia.ia_addr = cf->cf_loc[IICCF_ADDR];
108: ia.ia_size = cf->cf_loc[IICCF_SIZE];
1.6 jmcneill 109: ia.ia_type = sc->sc_type;
1.1 thorpej 110:
1.19.4.3! yamt 111: ia.ia_name = NULL;
! 112: ia.ia_ncompat = 0;
! 113: ia.ia_compat = NULL;
! 114:
1.1 thorpej 115: if (config_match(parent, cf, &ia) > 0)
116: config_attach(parent, cf, &ia, iic_print);
117:
118: return (0);
119: }
120:
121: static int
1.19.4.1 yamt 122: iic_match(device_t parent, cfdata_t cf, void *aux)
1.1 thorpej 123: {
124:
1.8 drochner 125: return (1);
1.1 thorpej 126: }
127:
128: static void
1.19.4.1 yamt 129: iic_attach(device_t parent, device_t self, void *aux)
1.1 thorpej 130: {
1.7 thorpej 131: struct iic_softc *sc = device_private(self);
1.1 thorpej 132: struct i2cbus_attach_args *iba = aux;
1.19.4.3! yamt 133: prop_array_t child_devices;
! 134: char *buf;
1.14 ad 135: i2c_tag_t ic;
136: int rv;
1.1 thorpej 137:
138: aprint_naive(": I2C bus\n");
139: aprint_normal(": I2C bus\n");
140:
141: sc->sc_tag = iba->iba_tag;
1.6 jmcneill 142: sc->sc_type = iba->iba_type;
1.14 ad 143: ic = sc->sc_tag;
1.19 cegger 144: ic->ic_devname = device_xname(self);
1.11 jmcneill 145:
1.13 jmcneill 146: LIST_INIT(&(sc->sc_tag->ic_list));
147: LIST_INIT(&(sc->sc_tag->ic_proc_list));
1.14 ad 148:
149: rv = kthread_create(PRI_NONE, 0, NULL, iic_smbus_intr_thread,
150: ic, &ic->ic_intr_thread, "%s", ic->ic_devname);
151: if (rv)
1.19.4.1 yamt 152: aprint_error_dev(self, "unable to create intr thread\n");
1.1 thorpej 153:
1.19.4.2 yamt 154: #if I2C_SCAN
1.15 riz 155: if (sc->sc_type == I2C_TYPE_SMBUS) {
1.19.4.2 yamt 156: int err;
1.16 joerg 157: int found = 0;
158: i2c_addr_t addr;
1.19.4.2 yamt 159: uint8_t val;
160:
161: for (addr = 0x09; addr < 0x78; addr++) {
162: /*
163: * Skip certain i2c addresses:
164: * 0x00 General Call / START
165: * 0x01 CBUS Address
166: * 0x02 Different Bus format
167: * 0x03 - 0x07 Reserved
168: * 0x08 Host Address
169: * 0x0c Alert Response Address
170: * 0x28 ACCESS.Bus host
171: * 0x37 ACCESS.Bus default address
172: * 0x48 - 0x4b Prototypes
173: * 0x61 Device Default Address
174: * 0x78 - 0x7b 10-bit addresses
175: * 0x7c - 0x7f Reserved
176: *
177: * Some of these are skipped by judicious selection
178: * of the range of the above for (;;) statement.
179: *
180: * if (addr <= 0x08 || addr >= 0x78)
181: * continue;
182: */
183: if (addr == 0x0c || addr == 0x28 || addr == 0x37 ||
184: addr == 0x61 || (addr & 0x7c) == 0x48)
185: continue;
1.16 joerg 186:
1.15 riz 187: iic_acquire_bus(ic, 0);
1.19.4.2 yamt 188: /*
189: * Use SMBus quick_write command to detect most
190: * addresses; should avoid hanging the bus on
191: * some write-only devices (like clocks that show
192: * up at address 0x69)
193: *
194: * XXX The quick_write() is allegedly known to
195: * XXX corrupt the Atmel AT24RF08 EEPROM found
196: * XXX on some IBM Thinkpads!
197: */
198: if ((addr & 0xf8) == 0x30 ||
199: (addr & 0xf0) == 0x50)
200: err = iic_smbus_receive_byte(ic, addr, &val, 0);
201: else
202: err = iic_smbus_quick_write(ic, addr, 0);
203: if (err == 0) {
1.15 riz 204: if (found == 0)
205: aprint_normal("%s: devices at",
206: ic->ic_devname);
207: found++;
208: aprint_normal(" 0x%02x", addr);
209: }
210: iic_release_bus(ic, 0);
211: }
212: if (found == 0)
213: aprint_normal("%s: no devices found", ic->ic_devname);
214: aprint_normal("\n");
215: }
1.16 joerg 216: #endif
1.15 riz 217:
1.17 jmcneill 218: if (!pmf_device_register(self, NULL, NULL))
219: aprint_error_dev(self, "couldn't establish power handler\n");
220:
1.19.4.3! yamt 221: child_devices = prop_dictionary_get(device_properties(parent),
! 222: "i2c-child-devices");
! 223: if (child_devices) {
! 224: unsigned int i, count;
! 225: prop_dictionary_t dev;
! 226: prop_data_t cdata;
! 227: uint32_t addr, size;
! 228: uint64_t cookie;
! 229: const char *name;
! 230: struct i2c_attach_args ia;
! 231: int loc[2];
! 232:
! 233: memset(loc, 0, sizeof loc);
! 234: count = prop_array_count(child_devices);
! 235: for (i = 0; i < count; i++) {
! 236: dev = prop_array_get(child_devices, i);
! 237: if (!dev) continue;
! 238: if (!prop_dictionary_get_cstring_nocopy(
! 239: dev, "name", &name))
! 240: continue;
! 241: if (!prop_dictionary_get_uint32(dev, "addr", &addr))
! 242: continue;
! 243: if (!prop_dictionary_get_uint64(dev, "cookie", &cookie))
! 244: cookie = 0;
! 245: loc[0] = addr;
! 246: if (prop_dictionary_get_uint32(dev, "size", &size))
! 247: loc[1] = size;
! 248: else
! 249: loc[1] = -1;
! 250:
! 251: memset(&ia, 0, sizeof ia);
! 252: ia.ia_addr = addr;
! 253: ia.ia_type = sc->sc_type;
! 254: ia.ia_tag = ic;
! 255: ia.ia_name = name;
! 256: ia.ia_cookie = cookie;
! 257:
! 258: buf = NULL;
! 259: cdata = prop_dictionary_get(dev, "compatible");
! 260: if (cdata)
! 261: iic_fill_compat(&ia,
! 262: prop_data_data_nocopy(cdata),
! 263: prop_data_size(cdata), &buf);
! 264:
! 265: config_found_sm_loc(self, "iic", loc, &ia,
! 266: iic_print_direct, NULL);
! 267:
! 268: if (ia.ia_compat)
! 269: free(ia.ia_compat, M_TEMP);
! 270: if (buf)
! 271: free(buf, M_TEMP);
! 272: }
! 273: } else {
! 274: /*
! 275: * Attach all i2c devices described in the kernel
! 276: * configuration file.
! 277: */
! 278: config_search_ia(iic_search, self, "iic", NULL);
! 279: }
1.1 thorpej 280: }
281:
1.11 jmcneill 282: static void
1.14 ad 283: iic_smbus_intr_thread(void *aux)
1.11 jmcneill 284: {
285: i2c_tag_t ic;
286: struct ic_intr_list *il;
287: int rv;
288:
289: ic = (i2c_tag_t)aux;
290: ic->ic_running = 1;
291: ic->ic_pending = 0;
292:
293: while (ic->ic_running) {
294: if (ic->ic_pending == 0)
295: rv = tsleep(ic, PZERO, "iicintr", hz);
296: if (ic->ic_pending > 0) {
297: LIST_FOREACH(il, &(ic->ic_proc_list), il_next) {
298: (*il->il_intr)(il->il_intrarg);
299: }
300: ic->ic_pending--;
301: }
302: }
303:
304: kthread_exit(0);
305: }
306:
307: void *
308: iic_smbus_intr_establish(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
309: {
310: struct ic_intr_list *il;
311:
312: il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
313: if (il == NULL)
314: return NULL;
315:
316: il->il_intr = intr;
317: il->il_intrarg = intrarg;
318:
319: LIST_INSERT_HEAD(&(ic->ic_list), il, il_next);
320:
321: return il;
322: }
323:
324: void
325: iic_smbus_intr_disestablish(i2c_tag_t ic, void *hdl)
326: {
327: struct ic_intr_list *il;
328:
329: il = (struct ic_intr_list *)hdl;
330:
331: LIST_REMOVE(il, il_next);
332: free(il, M_DEVBUF);
333:
334: return;
335: }
336:
337: void *
338: iic_smbus_intr_establish_proc(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
339: {
340: struct ic_intr_list *il;
341:
342: il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
343: if (il == NULL)
344: return NULL;
345:
346: il->il_intr = intr;
347: il->il_intrarg = intrarg;
348:
349: LIST_INSERT_HEAD(&(ic->ic_proc_list), il, il_next);
350:
351: return il;
352: }
353:
354: void
355: iic_smbus_intr_disestablish_proc(i2c_tag_t ic, void *hdl)
356: {
357: struct ic_intr_list *il;
358:
359: il = (struct ic_intr_list *)hdl;
360:
361: LIST_REMOVE(il, il_next);
362: free(il, M_DEVBUF);
363:
364: return;
365: }
366:
367: int
368: iic_smbus_intr(i2c_tag_t ic)
369: {
370: struct ic_intr_list *il;
371:
372: LIST_FOREACH(il, &(ic->ic_list), il_next) {
373: (*il->il_intr)(il->il_intrarg);
374: }
375:
376: ic->ic_pending++;
377: wakeup(ic);
378:
379: return 1;
380: }
381:
1.19.4.3! yamt 382: static void
! 383: iic_fill_compat(struct i2c_attach_args *ia, const char *compat, size_t len,
! 384: char **buffer)
! 385: {
! 386: int count, i;
! 387: const char *c, *start, **ptr;
! 388:
! 389: *buffer = NULL;
! 390: for (i = count = 0, c = compat; i < len; i++, c++)
! 391: if (*c == 0)
! 392: count++;
! 393: count += 2;
! 394: ptr = malloc(sizeof(char*)*count, M_TEMP, M_WAITOK);
! 395: if (!ptr) return;
! 396:
! 397: for (i = count = 0, start = c = compat; i < len; i++, c++) {
! 398: if (*c == 0) {
! 399: ptr[count++] = start;
! 400: start = c+1;
! 401: }
! 402: }
! 403: if (start < compat+len) {
! 404: /* last string not 0 terminated */
! 405: size_t l = c-start;
! 406: *buffer = malloc(l+1, M_TEMP, M_WAITOK);
! 407: memcpy(*buffer, start, l);
! 408: (*buffer)[l] = 0;
! 409: ptr[count++] = *buffer;
! 410: }
! 411: ptr[count] = NULL;
! 412:
! 413: ia->ia_compat = ptr;
! 414: ia->ia_ncompat = count;
! 415: }
! 416:
! 417: int
! 418: iic_compat_match(struct i2c_attach_args *ia, const char ** compats)
! 419: {
! 420: int i;
! 421:
! 422: for (; compats && *compats; compats++) {
! 423: for (i = 0; i < ia->ia_ncompat; i++) {
! 424: if (strcmp(*compats, ia->ia_compat[i]) == 0)
! 425: return 1;
! 426: }
! 427: }
! 428: return 0;
! 429: }
! 430:
! 431:
1.19.4.1 yamt 432: CFATTACH_DECL_NEW(iic, sizeof(struct iic_softc),
1.1 thorpej 433: iic_match, iic_attach, NULL, NULL);
CVSweb <webmaster@jp.NetBSD.org>