Sync usage with man page.
[netbsd-mini2440.git] / sys / dev / i2c / i2c.c
blobb87436b942d6a7eb406f025036619ce2d50223ff
1 /* $NetBSD: i2c.c,v 1.22 2008/09/29 22:55:08 pgoyette Exp $ */
3 /*
4 * Copyright (c) 2003 Wasabi Systems, Inc.
5 * All rights reserved.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
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.
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.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: i2c.c,v 1.22 2008/09/29 22:55:08 pgoyette Exp $");
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>
46 #include <sys/malloc.h>
47 #include <sys/kthread.h>
48 #include <sys/proc.h>
49 #include <sys/kernel.h>
51 #include <dev/i2c/i2cvar.h>
53 #include "locators.h"
54 #include <opt_i2cbus.h>
56 struct iic_softc {
57 i2c_tag_t sc_tag;
58 int sc_type;
61 static void iic_smbus_intr_thread(void *);
63 int
64 iicbus_print(void *aux, const char *pnp)
67 if (pnp != NULL)
68 aprint_normal("iic at %s", pnp);
70 return (UNCONF);
73 static int
74 iic_print(void *aux, const char *pnp)
76 struct i2c_attach_args *ia = aux;
78 if (ia->ia_addr != (i2c_addr_t)-1)
79 aprint_normal(" addr 0x%x", ia->ia_addr);
81 return (UNCONF);
84 static int
85 iic_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
87 struct iic_softc *sc = device_private(parent);
88 struct i2c_attach_args ia;
90 ia.ia_tag = sc->sc_tag;
91 ia.ia_addr = cf->cf_loc[IICCF_ADDR];
92 ia.ia_size = cf->cf_loc[IICCF_SIZE];
93 ia.ia_type = sc->sc_type;
95 if (config_match(parent, cf, &ia) > 0)
96 config_attach(parent, cf, &ia, iic_print);
98 return (0);
101 static int
102 iic_match(device_t parent, cfdata_t cf, void *aux)
105 return (1);
108 static void
109 iic_attach(device_t parent, device_t self, void *aux)
111 struct iic_softc *sc = device_private(self);
112 struct i2cbus_attach_args *iba = aux;
113 i2c_tag_t ic;
114 int rv;
116 aprint_naive(": I2C bus\n");
117 aprint_normal(": I2C bus\n");
119 sc->sc_tag = iba->iba_tag;
120 sc->sc_type = iba->iba_type;
121 ic = sc->sc_tag;
122 ic->ic_devname = device_xname(self);
124 LIST_INIT(&(sc->sc_tag->ic_list));
125 LIST_INIT(&(sc->sc_tag->ic_proc_list));
127 rv = kthread_create(PRI_NONE, 0, NULL, iic_smbus_intr_thread,
128 ic, &ic->ic_intr_thread, "%s", ic->ic_devname);
129 if (rv)
130 aprint_error_dev(self, "unable to create intr thread\n");
132 #if I2C_SCAN
133 if (sc->sc_type == I2C_TYPE_SMBUS) {
134 int err;
135 int found = 0;
136 i2c_addr_t addr;
137 uint8_t val;
139 for (addr = 0x09; addr < 0x78; addr++) {
141 * Skip certain i2c addresses:
142 * 0x00 General Call / START
143 * 0x01 CBUS Address
144 * 0x02 Different Bus format
145 * 0x03 - 0x07 Reserved
146 * 0x08 Host Address
147 * 0x0c Alert Response Address
148 * 0x28 ACCESS.Bus host
149 * 0x37 ACCESS.Bus default address
150 * 0x48 - 0x4b Prototypes
151 * 0x61 Device Default Address
152 * 0x78 - 0x7b 10-bit addresses
153 * 0x7c - 0x7f Reserved
155 * Some of these are skipped by judicious selection
156 * of the range of the above for (;;) statement.
158 * if (addr <= 0x08 || addr >= 0x78)
159 * continue;
161 if (addr == 0x0c || addr == 0x28 || addr == 0x37 ||
162 addr == 0x61 || (addr & 0x7c) == 0x48)
163 continue;
165 iic_acquire_bus(ic, 0);
167 * Use SMBus quick_write command to detect most
168 * addresses; should avoid hanging the bus on
169 * some write-only devices (like clocks that show
170 * up at address 0x69)
172 * XXX The quick_write() is allegedly known to
173 * XXX corrupt the Atmel AT24RF08 EEPROM found
174 * XXX on some IBM Thinkpads!
176 if ((addr & 0xf8) == 0x30 ||
177 (addr & 0xf0) == 0x50)
178 err = iic_smbus_receive_byte(ic, addr, &val, 0);
179 else
180 err = iic_smbus_quick_write(ic, addr, 0);
181 if (err == 0) {
182 if (found == 0)
183 aprint_normal("%s: devices at",
184 ic->ic_devname);
185 found++;
186 aprint_normal(" 0x%02x", addr);
188 iic_release_bus(ic, 0);
190 if (found == 0)
191 aprint_normal("%s: no devices found", ic->ic_devname);
192 aprint_normal("\n");
194 #endif
196 if (!pmf_device_register(self, NULL, NULL))
197 aprint_error_dev(self, "couldn't establish power handler\n");
200 * Attach all i2c devices described in the kernel
201 * configuration file.
203 config_search_ia(iic_search, self, "iic", NULL);
206 static void
207 iic_smbus_intr_thread(void *aux)
209 i2c_tag_t ic;
210 struct ic_intr_list *il;
211 int rv;
213 ic = (i2c_tag_t)aux;
214 ic->ic_running = 1;
215 ic->ic_pending = 0;
217 while (ic->ic_running) {
218 if (ic->ic_pending == 0)
219 rv = tsleep(ic, PZERO, "iicintr", hz);
220 if (ic->ic_pending > 0) {
221 LIST_FOREACH(il, &(ic->ic_proc_list), il_next) {
222 (*il->il_intr)(il->il_intrarg);
224 ic->ic_pending--;
228 kthread_exit(0);
231 void *
232 iic_smbus_intr_establish(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
234 struct ic_intr_list *il;
236 il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
237 if (il == NULL)
238 return NULL;
240 il->il_intr = intr;
241 il->il_intrarg = intrarg;
243 LIST_INSERT_HEAD(&(ic->ic_list), il, il_next);
245 return il;
248 void
249 iic_smbus_intr_disestablish(i2c_tag_t ic, void *hdl)
251 struct ic_intr_list *il;
253 il = (struct ic_intr_list *)hdl;
255 LIST_REMOVE(il, il_next);
256 free(il, M_DEVBUF);
258 return;
261 void *
262 iic_smbus_intr_establish_proc(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
264 struct ic_intr_list *il;
266 il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
267 if (il == NULL)
268 return NULL;
270 il->il_intr = intr;
271 il->il_intrarg = intrarg;
273 LIST_INSERT_HEAD(&(ic->ic_proc_list), il, il_next);
275 return il;
278 void
279 iic_smbus_intr_disestablish_proc(i2c_tag_t ic, void *hdl)
281 struct ic_intr_list *il;
283 il = (struct ic_intr_list *)hdl;
285 LIST_REMOVE(il, il_next);
286 free(il, M_DEVBUF);
288 return;
292 iic_smbus_intr(i2c_tag_t ic)
294 struct ic_intr_list *il;
296 LIST_FOREACH(il, &(ic->ic_list), il_next) {
297 (*il->il_intr)(il->il_intrarg);
300 ic->ic_pending++;
301 wakeup(ic);
303 return 1;
306 CFATTACH_DECL_NEW(iic, sizeof(struct iic_softc),
307 iic_match, iic_attach, NULL, NULL);