1 /* $NetBSD: at24cxx.c,v 1.2 2004/03/11 15:11:53 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Steve C. Woodford and 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/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/kernel.h> 42 #include <sys/fcntl.h> 43 #include <sys/uio.h> 44 #include <sys/conf.h> 45 #include <sys/proc.h> 46 #include <sys/event.h> 47 48 #include <machine/bus.h> 49 50 #include <dev/i2c/i2cvar.h> 51 #include <dev/i2c/at24cxxvar.h> 52 53 /* 54 * AT24Cxx EEPROM I2C address: 55 * 56 * 101 0xxx 57 */ 58 #define AT24CXX_ADDRMASK 0x78 59 #define AT24CXX_ADDR 0x50 60 61 #define AT24CXX_WRITE_CYCLE_MS 10 62 #define AT24CXX_ADDR_HI(a) (((a) >> 8) & 0x1f) 63 #define AT24CXX_ADDR_LO(a) ((a) & 0xff) 64 65 #include "seeprom.h" 66 67 #if NSEEPROM > 0 68 69 struct seeprom_softc { 70 struct device sc_dev; 71 i2c_tag_t sc_tag; 72 int sc_address; 73 int sc_size; 74 int sc_cmdlen; 75 int sc_open; 76 }; 77 78 static int seeprom_match(struct device *, struct cfdata *, void *); 79 static void seeprom_attach(struct device *, struct device *, void *); 80 81 CFATTACH_DECL(seeprom, sizeof(struct seeprom_softc), 82 seeprom_match, seeprom_attach, NULL, NULL); 83 extern struct cfdriver seeprom_cd; 84 85 dev_type_open(seeprom_open); 86 dev_type_close(seeprom_close); 87 dev_type_read(seeprom_read); 88 dev_type_write(seeprom_write); 89 90 const struct cdevsw seeprom_cdevsw = { 91 seeprom_open, seeprom_close, seeprom_read, seeprom_write, noioctl, 92 nostop, notty, nopoll, nommap, nokqfilter 93 }; 94 95 static int seeprom_wait_idle(struct seeprom_softc *); 96 97 98 static int 99 seeprom_match(struct device *parent, struct cfdata *cf, void *aux) 100 { 101 struct i2c_attach_args *ia = aux; 102 103 if ((ia->ia_addr & AT24CXX_ADDRMASK) == AT24CXX_ADDR) 104 return (1); 105 106 return (0); 107 } 108 109 static void 110 seeprom_attach(struct device *parent, struct device *self, void *aux) 111 { 112 struct seeprom_softc *sc = (struct seeprom_softc *)self; 113 struct i2c_attach_args *ia = aux; 114 115 sc->sc_tag = ia->ia_tag; 116 sc->sc_address = ia->ia_addr; 117 118 aprint_naive(": EEPROM\n"); 119 aprint_normal(": AT24Cxx EEPROM\n"); 120 121 /* 122 * The AT24C01A/02/04/08/16 EEPROMs use a 1 byte command 123 * word to select the offset into the EEPROM page. The 124 * AT24C04/08/16 decode fewer of the i2c address bits, 125 * using the bottom 1, 2, or 3 to select the 256-byte 126 * super-page. 127 * 128 * The AT24C32/64 EEPROMs use a 2 byte command word and 129 * decode all of the i2c address bits. 130 */ 131 sc->sc_size = ia->ia_size; 132 switch (sc->sc_size) { 133 case 128: /* 1Kbit */ 134 case 256: /* 2Kbit */ 135 case 512: /* 4Kbit */ 136 case 1024: /* 8Kbit */ 137 case 2048: /* 16Kbit */ 138 sc->sc_cmdlen = 1; 139 break; 140 141 case 4096: /* 32Kbit */ 142 case 8192: /* 64Kbit */ 143 sc->sc_cmdlen = 2; 144 break; 145 146 default: 147 /* 148 * Default to 2KB. If we happen to have a 2KB 149 * EEPROM this will allow us to access it. If we 150 * have a smaller one, the worst that can happen 151 * is that we end up trying to read a different 152 * EEPROM on the bus when accessing it. 153 * 154 * Obviously this will not work for 4KB or 8KB 155 * EEPROMs, but them's the breaks. 156 */ 157 aprint_error("%s: invalid size specified; " 158 "assuming 2KB (16Kb)\n", sc->sc_dev.dv_xname); 159 sc->sc_size = 2048; 160 sc->sc_cmdlen = 1; 161 } 162 163 sc->sc_open = 0; 164 } 165 166 /*ARGSUSED*/ 167 int 168 seeprom_open(dev_t dev, int flag, int fmt, struct proc *p) 169 { 170 struct seeprom_softc *sc; 171 172 if ((sc = device_lookup(&seeprom_cd, minor(dev))) == NULL) 173 return (ENXIO); 174 175 /* XXX: Locking */ 176 177 if (sc->sc_open) 178 return (EBUSY); 179 180 sc->sc_open = 1; 181 return (0); 182 } 183 184 /*ARGSUSED*/ 185 int 186 seeprom_close(dev_t dev, int flag, int fmt, struct proc *p) 187 { 188 struct seeprom_softc *sc; 189 190 if ((sc = device_lookup(&seeprom_cd, minor(dev))) == NULL) 191 return (ENXIO); 192 193 sc->sc_open = 0; 194 return (0); 195 } 196 197 /*ARGSUSED*/ 198 int 199 seeprom_read(dev_t dev, struct uio *uio, int flags) 200 { 201 struct seeprom_softc *sc; 202 i2c_addr_t addr; 203 u_int8_t ch, cmdbuf[2]; 204 int a, error; 205 206 if ((sc = device_lookup(&seeprom_cd, minor(dev))) == NULL) 207 return (ENXIO); 208 209 if (uio->uio_offset >= sc->sc_size) 210 return (EINVAL); 211 212 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 213 return (error); 214 215 /* 216 * Even though the AT24Cxx EEPROMs support sequential 217 * reads within a page, some I2C controllers do not 218 * support anything other than single-byte transfers, 219 * so we're stuck with this lowest-common-denominator. 220 */ 221 222 while (uio->uio_resid > 0 && uio->uio_offset < sc->sc_size) { 223 a = (int)uio->uio_offset; 224 if (sc->sc_cmdlen == 1) { 225 addr = sc->sc_address + (a >> 8); 226 cmdbuf[0] = a & 0xff; 227 } else { 228 addr = sc->sc_address; 229 cmdbuf[0] = AT24CXX_ADDR_HI(a); 230 cmdbuf[1] = AT24CXX_ADDR_LO(a); 231 } 232 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 233 addr, cmdbuf, sc->sc_cmdlen, 234 &ch, 1, 0)) != 0) { 235 iic_release_bus(sc->sc_tag, 0); 236 printf("%s: seeprom_read: byte read failed at 0x%x\n", 237 sc->sc_dev.dv_xname, a); 238 return (error); 239 } 240 if ((error = uiomove(&ch, 1, uio)) != 0) { 241 iic_release_bus(sc->sc_tag, 0); 242 return (error); 243 } 244 } 245 246 iic_release_bus(sc->sc_tag, 0); 247 248 return (0); 249 } 250 251 /*ARGSUSED*/ 252 int 253 seeprom_write(dev_t dev, struct uio *uio, int flags) 254 { 255 struct seeprom_softc *sc; 256 i2c_addr_t addr; 257 u_int8_t ch, cmdbuf[2]; 258 int a, error; 259 260 if ((sc = device_lookup(&seeprom_cd, minor(dev))) == NULL) 261 return (ENXIO); 262 263 if (uio->uio_offset >= sc->sc_size) 264 return (EINVAL); 265 266 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 267 return (error); 268 269 /* 270 * See seeprom_read() for why we don't use sequential 271 * writes within a page. 272 */ 273 274 while (uio->uio_resid > 0 && uio->uio_offset < sc->sc_size) { 275 a = (int)uio->uio_offset; 276 if (sc->sc_cmdlen == 1) { 277 addr = sc->sc_address + (a >> 8); 278 cmdbuf[0] = a & 0xff; 279 } else { 280 addr = sc->sc_address; 281 cmdbuf[0] = AT24CXX_ADDR_HI(a); 282 cmdbuf[1] = AT24CXX_ADDR_LO(a); 283 } 284 if ((error = uiomove(&ch, 1, uio)) != 0) { 285 iic_release_bus(sc->sc_tag, 0); 286 return (error); 287 } 288 if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 289 addr, cmdbuf, sc->sc_cmdlen, 290 &ch, 1, 0)) != 0) { 291 iic_release_bus(sc->sc_tag, 0); 292 printf("%s: seeprom_write: byte write failed at 0x%x\n", 293 sc->sc_dev.dv_xname, a); 294 return (error); 295 } 296 297 /* Wait until the device commits the byte. */ 298 if ((error = seeprom_wait_idle(sc)) != 0) { 299 iic_release_bus(sc->sc_tag, 0); 300 return (error); 301 } 302 } 303 304 iic_release_bus(sc->sc_tag, 0); 305 306 return (0); 307 } 308 309 static int 310 seeprom_wait_idle(struct seeprom_softc *sc) 311 { 312 uint8_t cmdbuf[2] = { 0, 0 }; 313 int rv, timeout; 314 u_int8_t dummy; 315 316 timeout = (1000 / hz) / AT24CXX_WRITE_CYCLE_MS; 317 if (timeout == 0) 318 timeout = 1; 319 320 delay(10); 321 322 /* 323 * Read the byte at address 0. This is just a dummy 324 * read to wait for the EEPROM's write cycle to complete. 325 */ 326 while (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 327 cmdbuf, sc->sc_cmdlen, &dummy, 1, 0)) { 328 rv = tsleep(sc, PRIBIO | PCATCH, "seepromwr", timeout); 329 if (rv != EWOULDBLOCK) 330 return (rv); 331 } 332 333 return (0); 334 } 335 336 #endif /* NSEEPROM > 0 */ 337 338 int 339 seeprom_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset, int devsize, 340 u_int8_t *rvp, size_t len) 341 { 342 i2c_addr_t addr; 343 int cmdlen; 344 uint8_t cmdbuf[2]; 345 346 if (len == 0) 347 return (0); 348 349 /* We are very forgiving about devsize during bootstrap. */ 350 cmdlen = (devsize >= 4096) ? 2 : 1; 351 352 if (iic_acquire_bus(tag, I2C_F_POLL) != 0) 353 return (-1); 354 355 while (len) { 356 if (cmdlen == 1) { 357 addr = i2caddr + (offset >> 8); 358 cmdbuf[0] = offset & 0xff; 359 } else { 360 addr = i2caddr; 361 cmdbuf[0] = AT24CXX_ADDR_HI(offset); 362 cmdbuf[1] = AT24CXX_ADDR_LO(offset); 363 } 364 365 /* Read a single byte. */ 366 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, 367 cmdbuf, cmdlen, rvp, 1, I2C_F_POLL)) { 368 iic_release_bus(tag, I2C_F_POLL); 369 return (-1); 370 } 371 372 len--; 373 rvp++; 374 offset++; 375 } 376 377 iic_release_bus(tag, I2C_F_POLL); 378 return (0); 379 } 380