1 /* $NetBSD: i2c.c,v 1.20 2008/05/04 15:26:29 xtraeme Exp $ */ 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 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: i2c.c,v 1.20 2008/05/04 15:26:29 xtraeme Exp $"); 40 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> 50 51 #include <dev/i2c/i2cvar.h> 52 53 #include "locators.h" 54 55 struct iic_softc { 56 i2c_tag_t sc_tag; 57 int sc_type; 58 }; 59 60 static void iic_smbus_intr_thread(void *); 61 62 int 63 iicbus_print(void *aux, const char *pnp) 64 { 65 66 if (pnp != NULL) 67 aprint_normal("iic at %s", pnp); 68 69 return (UNCONF); 70 } 71 72 static int 73 iic_print(void *aux, const char *pnp) 74 { 75 struct i2c_attach_args *ia = aux; 76 77 if (ia->ia_addr != (i2c_addr_t)-1) 78 aprint_normal(" addr 0x%x", ia->ia_addr); 79 80 return (UNCONF); 81 } 82 83 static int 84 iic_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 85 { 86 struct iic_softc *sc = device_private(parent); 87 struct i2c_attach_args ia; 88 89 ia.ia_tag = sc->sc_tag; 90 ia.ia_addr = cf->cf_loc[IICCF_ADDR]; 91 ia.ia_size = cf->cf_loc[IICCF_SIZE]; 92 ia.ia_type = sc->sc_type; 93 94 if (config_match(parent, cf, &ia) > 0) 95 config_attach(parent, cf, &ia, iic_print); 96 97 return (0); 98 } 99 100 static int 101 iic_match(device_t parent, cfdata_t cf, void *aux) 102 { 103 104 return (1); 105 } 106 107 static void 108 iic_attach(device_t parent, device_t self, void *aux) 109 { 110 struct iic_softc *sc = device_private(self); 111 struct i2cbus_attach_args *iba = aux; 112 i2c_tag_t ic; 113 int rv; 114 115 aprint_naive(": I2C bus\n"); 116 aprint_normal(": I2C bus\n"); 117 118 sc->sc_tag = iba->iba_tag; 119 sc->sc_type = iba->iba_type; 120 ic = sc->sc_tag; 121 ic->ic_devname = device_xname(self); 122 123 LIST_INIT(&(sc->sc_tag->ic_list)); 124 LIST_INIT(&(sc->sc_tag->ic_proc_list)); 125 126 rv = kthread_create(PRI_NONE, 0, NULL, iic_smbus_intr_thread, 127 ic, &ic->ic_intr_thread, "%s", ic->ic_devname); 128 if (rv) 129 aprint_error_dev(self, "unable to create intr thread\n"); 130 131 #if notyet 132 if (sc->sc_type == I2C_TYPE_SMBUS) { 133 int found = 0; 134 i2c_addr_t addr; 135 uint8_t cmd = 0, val; 136 137 for (addr = 0x0; addr < 0x80; addr++) { 138 iic_acquire_bus(ic, 0); 139 if (iic_exec(ic, I2C_OP_READ_WITH_STOP, addr, 140 &cmd, 1, &val, 1, 0) == 0) { 141 if (found == 0) 142 aprint_normal("%s: devices at", 143 ic->ic_devname); 144 found++; 145 aprint_normal(" 0x%02x", addr); 146 } 147 iic_release_bus(ic, 0); 148 } 149 if (found == 0) 150 aprint_normal("%s: no devices found", ic->ic_devname); 151 aprint_normal("\n"); 152 } 153 #endif 154 155 if (!pmf_device_register(self, NULL, NULL)) 156 aprint_error_dev(self, "couldn't establish power handler\n"); 157 158 /* 159 * Attach all i2c devices described in the kernel 160 * configuration file. 161 */ 162 config_search_ia(iic_search, self, "iic", NULL); 163 } 164 165 static void 166 iic_smbus_intr_thread(void *aux) 167 { 168 i2c_tag_t ic; 169 struct ic_intr_list *il; 170 int rv; 171 172 ic = (i2c_tag_t)aux; 173 ic->ic_running = 1; 174 ic->ic_pending = 0; 175 176 while (ic->ic_running) { 177 if (ic->ic_pending == 0) 178 rv = tsleep(ic, PZERO, "iicintr", hz); 179 if (ic->ic_pending > 0) { 180 LIST_FOREACH(il, &(ic->ic_proc_list), il_next) { 181 (*il->il_intr)(il->il_intrarg); 182 } 183 ic->ic_pending--; 184 } 185 } 186 187 kthread_exit(0); 188 } 189 190 void * 191 iic_smbus_intr_establish(i2c_tag_t ic, int (*intr)(void *), void *intrarg) 192 { 193 struct ic_intr_list *il; 194 195 il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK); 196 if (il == NULL) 197 return NULL; 198 199 il->il_intr = intr; 200 il->il_intrarg = intrarg; 201 202 LIST_INSERT_HEAD(&(ic->ic_list), il, il_next); 203 204 return il; 205 } 206 207 void 208 iic_smbus_intr_disestablish(i2c_tag_t ic, void *hdl) 209 { 210 struct ic_intr_list *il; 211 212 il = (struct ic_intr_list *)hdl; 213 214 LIST_REMOVE(il, il_next); 215 free(il, M_DEVBUF); 216 217 return; 218 } 219 220 void * 221 iic_smbus_intr_establish_proc(i2c_tag_t ic, int (*intr)(void *), void *intrarg) 222 { 223 struct ic_intr_list *il; 224 225 il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK); 226 if (il == NULL) 227 return NULL; 228 229 il->il_intr = intr; 230 il->il_intrarg = intrarg; 231 232 LIST_INSERT_HEAD(&(ic->ic_proc_list), il, il_next); 233 234 return il; 235 } 236 237 void 238 iic_smbus_intr_disestablish_proc(i2c_tag_t ic, void *hdl) 239 { 240 struct ic_intr_list *il; 241 242 il = (struct ic_intr_list *)hdl; 243 244 LIST_REMOVE(il, il_next); 245 free(il, M_DEVBUF); 246 247 return; 248 } 249 250 int 251 iic_smbus_intr(i2c_tag_t ic) 252 { 253 struct ic_intr_list *il; 254 255 LIST_FOREACH(il, &(ic->ic_list), il_next) { 256 (*il->il_intr)(il->il_intrarg); 257 } 258 259 ic->ic_pending++; 260 wakeup(ic); 261 262 return 1; 263 } 264 265 CFATTACH_DECL_NEW(iic, sizeof(struct iic_softc), 266 iic_match, iic_attach, NULL, NULL); 267