at91_twi.c revision 157563
1/*- 2 * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include <sys/cdefs.h> 26__FBSDID("$FreeBSD: head/sys/arm/at91/at91_twi.c 157563 2006-04-06 04:31:19Z imp $"); 27 28#include <sys/param.h> 29#include <sys/systm.h> 30#include <sys/bus.h> 31#include <sys/conf.h> 32#include <sys/kernel.h> 33#include <sys/lock.h> 34#include <sys/mbuf.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/mutex.h> 38#include <sys/rman.h> 39#include <machine/bus.h> 40 41#include <arm/at91/at91rm92reg.h> 42#include <arm/at91/at91_twireg.h> 43 44#include <dev/iicbus/iiconf.h> 45#include <dev/iicbus/iicbus.h> 46#include "iicbus_if.h" 47 48struct at91_twi_softc 49{ 50 device_t dev; /* Myself */ 51 void *intrhand; /* Interrupt handle */ 52 struct resource *irq_res; /* IRQ resource */ 53 struct resource *mem_res; /* Memory resource */ 54 struct mtx sc_mtx; /* basically a perimeter lock */ 55 int flags; 56#define RXRDY 4 57#define TXRDY 0x10 58 uint32_t cwgr; 59 int sc_started; 60 int twi_addr; 61 device_t iicbus; 62}; 63 64static inline uint32_t 65RD4(struct at91_twi_softc *sc, bus_size_t off) 66{ 67 return bus_read_4(sc->mem_res, off); 68} 69 70static inline void 71WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val) 72{ 73 bus_write_4(sc->mem_res, off, val); 74} 75 76#define AT91_TWI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 77#define AT91_TWI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 78#define AT91_TWI_LOCK_INIT(_sc) \ 79 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 80 "twi", MTX_DEF) 81#define AT91_TWI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 82#define AT91_TWI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 83#define AT91_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 84#define TWI_DEF_CLK 100000 85 86static devclass_t at91_twi_devclass; 87 88/* bus entry points */ 89 90static int at91_twi_probe(device_t dev); 91static int at91_twi_attach(device_t dev); 92static int at91_twi_detach(device_t dev); 93static void at91_twi_intr(void *); 94 95/* helper routines */ 96static int at91_twi_activate(device_t dev); 97static void at91_twi_deactivate(device_t dev); 98 99static int 100at91_twi_probe(device_t dev) 101{ 102 device_set_desc(dev, "TWI"); 103 return (0); 104} 105 106static int 107at91_twi_attach(device_t dev) 108{ 109 struct at91_twi_softc *sc = device_get_softc(dev); 110 int err; 111 112 sc->dev = dev; 113 err = at91_twi_activate(dev); 114 if (err) 115 goto out; 116 117 AT91_TWI_LOCK_INIT(sc); 118 119 /* 120 * Activate the interrupt 121 */ 122 err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 123 at91_twi_intr, sc, &sc->intrhand); 124 if (err) { 125 AT91_TWI_LOCK_DESTROY(sc); 126 goto out; 127 } 128 sc->cwgr = TWI_CWGR_CKDIV(8 * AT91C_MASTER_CLOCK / 90000) | 129 TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) | 130 TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK)); 131 132 WR4(sc, TWI_CR, TWI_CR_SWRST); 133 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 134 WR4(sc, TWI_CWGR, sc->cwgr); 135 136 WR4(sc, TWI_IER, TWI_SR_RXRDY | TWI_SR_OVRE | TWI_SR_UNRE | 137 TWI_SR_NACK); 138 139 if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) 140 device_printf(dev, "could not allocate iicbus instance\n"); 141 142 /* probe and attach the iicbus */ 143 bus_generic_attach(dev); 144 145out:; 146 if (err) 147 at91_twi_deactivate(dev); 148 return (err); 149} 150 151static int 152at91_twi_detach(device_t dev) 153{ 154 struct at91_twi_softc *sc; 155 int rv; 156 157 sc = device_get_softc(dev); 158 at91_twi_deactivate(dev); 159 if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0) 160 return (rv); 161 162 return (0); 163} 164 165static int 166at91_twi_activate(device_t dev) 167{ 168 struct at91_twi_softc *sc; 169 int rid; 170 171 sc = device_get_softc(dev); 172 rid = 0; 173 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 174 RF_ACTIVE); 175 if (sc->mem_res == NULL) 176 goto errout; 177 rid = 0; 178 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 179 RF_ACTIVE); 180 if (sc->mem_res == NULL) 181 goto errout; 182 return (0); 183errout: 184 at91_twi_deactivate(dev); 185 return (ENOMEM); 186} 187 188static void 189at91_twi_deactivate(device_t dev) 190{ 191 struct at91_twi_softc *sc; 192 193 sc = device_get_softc(dev); 194 if (sc->intrhand) 195 bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 196 sc->intrhand = 0; 197 bus_generic_detach(sc->dev); 198 if (sc->mem_res) 199 bus_release_resource(dev, SYS_RES_IOPORT, 200 rman_get_rid(sc->mem_res), sc->mem_res); 201 sc->mem_res = 0; 202 if (sc->irq_res) 203 bus_release_resource(dev, SYS_RES_IRQ, 204 rman_get_rid(sc->irq_res), sc->irq_res); 205 sc->irq_res = 0; 206 return; 207} 208 209static void 210at91_twi_intr(void *xsc) 211{ 212 struct at91_twi_softc *sc = xsc; 213 uint32_t status; 214 215 /* Reading the status also clears the interrupt */ 216 status = RD4(sc, TWI_SR); 217 printf("status %x\n", status); 218 if (status == 0) 219 return; 220 AT91_TWI_LOCK(sc); 221 if (status & TWI_SR_RXRDY) 222 sc->flags |= RXRDY; 223 if (status & TWI_SR_TXRDY) 224 sc->flags |= TXRDY; 225 AT91_TWI_UNLOCK(sc); 226 wakeup(sc); 227 return; 228} 229 230static int 231at91_twi_wait_stop_done(struct at91_twi_softc *sc) 232{ 233 int err = 0; 234 235 while (!(RD4(sc, TWI_SR) & TWI_SR_TXCOMP)) 236 continue; 237 return (err); 238} 239 240/* 241 * Stop the transfer by entering a STOP state on the iic bus. For read 242 * operations, we've already entered the STOP state, since we need to do 243 * that to read the last character. For write operations, we need to 244 * wait for the TXCOMP bit to turn on before returning. 245 */ 246static int 247at91_twi_stop(device_t dev) 248{ 249 struct at91_twi_softc *sc; 250 int err = 0; 251 252 sc = device_get_softc(dev); 253 if (sc->sc_started) { 254 WR4(sc, TWI_CR, TWI_CR_STOP); 255 err = at91_twi_wait_stop_done(sc); 256 } 257 return (err); 258} 259 260/* 261 * enter a START condition without requiring the device to be in a STOP 262 * state. 263 */ 264static int 265at91_twi_repeated_start(device_t dev, u_char slave, int timeout) 266{ 267 struct at91_twi_softc *sc; 268 269 sc = device_get_softc(dev); 270 WR4(sc, TWI_MMR, TWI_MMR_DADR(slave)); 271 WR4(sc, TWI_CR, TWI_CR_START); 272 sc->sc_started = 1; 273 return (0); 274} 275 276/* 277 * enter a START condition from an idle state. 278 */ 279static int 280at91_twi_start(device_t dev, u_char slave, int timeout) 281{ 282 struct at91_twi_softc *sc; 283 284 sc = device_get_softc(dev); 285 WR4(sc, TWI_MMR, TWI_MMR_DADR(slave)); 286 WR4(sc, TWI_CR, TWI_CR_START); 287 sc->sc_started = 1; 288 return (0); 289} 290 291static int 292at91_twi_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */) 293{ 294 struct at91_twi_softc *sc; 295 uint8_t *walker; 296 int err = 0; 297 298 walker = buf; 299 sc = device_get_softc(dev); 300 WR4(sc, TWI_MMR, TWI_MMR_MWRITE | RD4(sc, TWI_MMR)); 301 AT91_TWI_LOCK(sc); 302 WR4(sc, TWI_IER, TWI_SR_TXRDY); 303 while (len--) { 304 WR4(sc, TWI_THR, *walker++); 305 while (!(sc->flags & TXRDY)) { 306 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr", 307 0); 308 if (err) 309 goto errout; 310 } 311 } 312errout:; 313 WR4(sc, TWI_IDR, TWI_SR_TXRDY); 314 AT91_TWI_UNLOCK(sc); 315 return (err); 316} 317 318static int 319at91_twi_read(device_t dev, char *buf, int len, int *read, int last, 320 int delay /* us */) 321{ 322 struct at91_twi_softc *sc; 323 char *walker; 324 int err = 0; 325 326 walker = buf; 327 sc = device_get_softc(dev); 328 AT91_TWI_LOCK(sc); 329 WR4(sc, TWI_MMR, ~TWI_MMR_MWRITE & RD4(sc, TWI_MMR)); 330 WR4(sc, TWI_IER, TWI_SR_RXRDY); 331 while (len-- > 0) { 332 err = 0; 333 while (!(sc->flags & RXRDY)) { 334 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird", 335 0); 336 if (err) 337 goto errout; 338 } 339 sc->flags &= ~RXRDY; 340 *walker++ = RD4(sc, TWI_RHR) & 0xff; 341 if (len == 1 && last) 342 break; 343 } 344 if (!last) 345 goto errout; 346 WR4(sc, TWI_CR, TWI_CR_STOP); 347 err = at91_twi_wait_stop_done(sc); 348 *walker = RD4(sc, TWI_RHR) & 0xff; 349 if (read) 350 *read = walker - buf; 351 sc->sc_started = 0; 352errout:; 353 WR4(sc, TWI_IDR, TWI_SR_RXRDY); 354 AT91_TWI_UNLOCK(sc); 355 return (err); 356} 357 358static int 359at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 360{ 361 struct at91_twi_softc *sc; 362 int ckdiv, rate; 363 364 sc = device_get_softc(dev); 365 if (oldaddr) 366 *oldaddr = sc->twi_addr; 367 if (addr != 0) 368 sc->twi_addr = 0; 369 else 370 sc->twi_addr = addr; 371 372 rate = 1; 373 374 /* 375 * 8 * is because "rate == 1" -> 4 clocks down, 4 clocks up. The 376 * speeds are for 1.5kb/s, 45kb/s and 90kb/s. 377 */ 378 switch (speed) { 379 case IIC_SLOW: 380 ckdiv = 8 * AT91C_MASTER_CLOCK / 1500; 381 break; 382 383 case IIC_FAST: 384 ckdiv = 8 * AT91C_MASTER_CLOCK / 45000; 385 break; 386 387 case IIC_UNKNOWN: 388 case IIC_FASTEST: 389 default: 390 ckdiv = 8 * AT91C_MASTER_CLOCK / 90000; 391 break; 392 } 393 394 sc->cwgr = TWI_CWGR_CKDIV(ckdiv) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(rate)) | 395 TWI_CWGR_CLDIV(TWI_CWGR_DIV(rate)); 396 WR4(sc, TWI_CR, TWI_CR_SWRST); 397 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 398 WR4(sc, TWI_CWGR, sc->cwgr); 399 400 return EIO; 401} 402 403static int 404at91_twi_callback(device_t dev, int index, caddr_t *data) 405{ 406 int error = 0; 407 408 switch (index) { 409 case IIC_REQUEST_BUS: 410 break; 411 412 case IIC_RELEASE_BUS: 413 break; 414 415 default: 416 error = EINVAL; 417 } 418 419 return (error); 420} 421 422static device_method_t at91_twi_methods[] = { 423 /* Device interface */ 424 DEVMETHOD(device_probe, at91_twi_probe), 425 DEVMETHOD(device_attach, at91_twi_attach), 426 DEVMETHOD(device_detach, at91_twi_detach), 427 428 /* iicbus interface */ 429 DEVMETHOD(iicbus_callback, at91_twi_callback), 430 DEVMETHOD(iicbus_repeated_start, at91_twi_repeated_start), 431 DEVMETHOD(iicbus_start, at91_twi_start), 432 DEVMETHOD(iicbus_stop, at91_twi_stop), 433 DEVMETHOD(iicbus_write, at91_twi_write), 434 DEVMETHOD(iicbus_read, at91_twi_read), 435 DEVMETHOD(iicbus_reset, at91_twi_rst_card), 436 { 0, 0 } 437}; 438 439static driver_t at91_twi_driver = { 440 "at91_twi", 441 at91_twi_methods, 442 sizeof(struct at91_twi_softc), 443}; 444 445DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, 0, 0); 446