at91_twi.c revision 155324
150479Speter/*- 220253Sjoerg * Copyright (c) 2006 M. Warner Losh. All rights reserved. 320253Sjoerg * 480029Sobrien * Redistribution and use in source and binary forms, with or without 544229Sdavidn * modification, are permitted provided that the following conditions 620253Sjoerg * are met: 720253Sjoerg * 1. Redistributions of source code must retain the above copyright 820253Sjoerg * notice, this list of conditions and the following disclaimer. 964918Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1064918Sgreen * notice, this list of conditions and the following disclaimer in the 1120253Sjoerg * documentation and/or other materials provided with the distribution. 1220253Sjoerg * 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 155324 2006-02-04 23:32:13Z 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#include <arm/at91/at91_twiio.h> 44 45struct at91_twi_softc 46{ 47 device_t dev; /* Myself */ 48 void *intrhand; /* Interrupt handle */ 49 struct resource *irq_res; /* IRQ resource */ 50 struct resource *mem_res; /* Memory resource */ 51 struct mtx sc_mtx; /* basically a perimeter lock */ 52 int flags; 53#define XFER_PENDING 1 /* true when transfer taking place */ 54#define OPENED 2 /* Device opened */ 55#define RXRDY 4 56#define TXCOMP 8 57#define TXRDY 0x10 58 struct cdev *cdev; 59 uint32_t cwgr; 60}; 61 62static inline uint32_t 63RD4(struct at91_twi_softc *sc, bus_size_t off) 64{ 65 return bus_read_4(sc->mem_res, off); 66} 67 68static inline void 69WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val) 70{ 71 bus_write_4(sc->mem_res, off, val); 72} 73 74#define AT91_TWI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 75#define AT91_TWI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 76#define AT91_TWI_LOCK_INIT(_sc) \ 77 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 78 "twi", MTX_DEF) 79#define AT91_TWI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 80#define AT91_TWI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 81#define AT91_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 82#define CDEV2SOFTC(dev) ((dev)->si_drv1) 83#define TWI_DEF_CLK 100000 84 85static devclass_t at91_twi_devclass; 86 87/* bus entry points */ 88 89static int at91_twi_probe(device_t dev); 90static int at91_twi_attach(device_t dev); 91static int at91_twi_detach(device_t dev); 92static void at91_twi_intr(void *); 93 94/* helper routines */ 95static int at91_twi_activate(device_t dev); 96static void at91_twi_deactivate(device_t dev); 97 98/* cdev routines */ 99static d_open_t at91_twi_open; 100static d_close_t at91_twi_close; 101static d_ioctl_t at91_twi_ioctl; 102 103static struct cdevsw at91_twi_cdevsw = 104{ 105 .d_version = D_VERSION, 106 .d_open = at91_twi_open, 107 .d_close = at91_twi_close, 108 .d_ioctl = at91_twi_ioctl 109}; 110 111static int 112at91_twi_probe(device_t dev) 113{ 114 device_set_desc(dev, "TWI"); 115 return (0); 116} 117 118static int 119at91_twi_attach(device_t dev) 120{ 121 struct at91_twi_softc *sc = device_get_softc(dev); 122 int err; 123 124 sc->dev = dev; 125 err = at91_twi_activate(dev); 126 if (err) 127 goto out; 128 129 AT91_TWI_LOCK_INIT(sc); 130 131 /* 132 * Activate the interrupt 133 */ 134 err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 135 at91_twi_intr, sc, &sc->intrhand); 136 if (err) { 137 AT91_TWI_LOCK_DESTROY(sc); 138 goto out; 139 } 140 sc->cdev = make_dev(&at91_twi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 141 "twi%d", device_get_unit(dev)); 142 if (sc->cdev == NULL) { 143 err = ENOMEM; 144 goto out; 145 } 146 sc->cdev->si_drv1 = sc; 147 sc->cwgr = TWI_CWGR_CKDIV(1) | 148 TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) | 149 TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK)); 150 151 WR4(sc, TWI_CR, TWI_CR_SWRST); 152 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 153 WR4(sc, TWI_CWGR, sc->cwgr); 154out:; 155 if (err) 156 at91_twi_deactivate(dev); 157 return (err); 158} 159 160static int 161at91_twi_detach(device_t dev) 162{ 163 return (EBUSY); /* XXX */ 164} 165 166static int 167at91_twi_activate(device_t dev) 168{ 169 struct at91_twi_softc *sc; 170 int rid; 171 172 sc = device_get_softc(dev); 173 rid = 0; 174 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 175 RF_ACTIVE); 176 if (sc->mem_res == NULL) 177 goto errout; 178 rid = 0; 179 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 180 RF_ACTIVE); 181 if (sc->mem_res == NULL) 182 goto errout; 183 return (0); 184errout: 185 at91_twi_deactivate(dev); 186 return (ENOMEM); 187} 188 189static void 190at91_twi_deactivate(device_t dev) 191{ 192 struct at91_twi_softc *sc; 193 194 sc = device_get_softc(dev); 195 if (sc->intrhand) 196 bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 197 sc->intrhand = 0; 198 bus_generic_detach(sc->dev); 199 if (sc->mem_res) 200 bus_release_resource(dev, SYS_RES_IOPORT, 201 rman_get_rid(sc->mem_res), sc->mem_res); 202 sc->mem_res = 0; 203 if (sc->irq_res) 204 bus_release_resource(dev, SYS_RES_IRQ, 205 rman_get_rid(sc->irq_res), sc->irq_res); 206 sc->irq_res = 0; 207 return; 208} 209 210static void 211at91_twi_intr(void *xsc) 212{ 213 struct at91_twi_softc *sc = xsc; 214 uint32_t status; 215 216 /* Reading the status also clears the interrupt */ 217 status = RD4(sc, TWI_SR); 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_TXCOMP) 224 sc->flags |= TXCOMP; 225 if (status & TWI_SR_TXRDY) 226 sc->flags |= TXRDY; 227 AT91_TWI_UNLOCK(sc); 228 wakeup(sc); 229 return; 230} 231 232static int 233at91_twi_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 234{ 235 struct at91_twi_softc *sc; 236 237 sc = CDEV2SOFTC(dev); 238 AT91_TWI_LOCK(sc); 239 if (!(sc->flags & OPENED)) { 240 sc->flags |= OPENED; 241 WR4(sc, TWI_IER, TWI_SR_TXCOMP | TWI_SR_RXRDY | TWI_SR_TXRDY | 242 TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); 243 } 244 AT91_TWI_UNLOCK(sc); 245 return (0); 246} 247 248static int 249at91_twi_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 250{ 251 struct at91_twi_softc *sc; 252 253 sc = CDEV2SOFTC(dev); 254 AT91_TWI_LOCK(sc); 255 sc->flags &= ~OPENED; 256 WR4(sc, TWI_IDR, TWI_SR_TXCOMP | TWI_SR_RXRDY | TWI_SR_TXRDY | 257 TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); 258 AT91_TWI_UNLOCK(sc); 259 return (0); 260} 261 262 263static int 264at91_twi_read_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr) 265{ 266 uint8_t *walker; 267 uint8_t buffer[256]; 268 size_t len; 269 int err = 0; 270 271 if (xfr->xfer_len > sizeof(buffer)) 272 return (EINVAL); 273 walker = buffer; 274 len = xfr->xfer_len; 275 RD4(sc, TWI_RHR); 276 // Master mode, with the right address and interal addr size 277 WR4(sc, TWI_MMR, TWI_MMR_IADRSZ(xfr->iadrsz) | TWI_MMR_MREAD | 278 TWI_MMR_DADR(xfr->dadr)); 279 WR4(sc, TWI_IADR, xfr->iadr); 280 WR4(sc, TWI_CR, TWI_CR_START); 281 while (len-- > 1) { 282 while (!(sc->flags & RXRDY)) { 283 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird", 284 0); 285 if (err) 286 return (err); 287 } 288 sc->flags &= ~RXRDY; 289 *walker++ = RD4(sc, TWI_RHR) & 0xff; 290 } 291 WR4(sc, TWI_CR, TWI_CR_STOP); 292 while (!(sc->flags & TXCOMP)) { 293 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird2", 0); 294 if (err) 295 return (err); 296 } 297 sc->flags &= ~TXCOMP; 298 *walker = RD4(sc, TWI_RHR) & 0xff; 299 if (xfr->xfer_buf) { 300 AT91_TWI_UNLOCK(sc); 301 err = copyout(buffer, xfr->xfer_buf, xfr->xfer_len); 302 AT91_TWI_LOCK(sc); 303 } 304 return (err); 305} 306 307static int 308at91_twi_write_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr) 309{ 310 uint8_t *walker; 311 uint8_t buffer[256]; 312 size_t len; 313 int err; 314 315 if (xfr->xfer_len > sizeof(buffer)) 316 return (EINVAL); 317 walker = buffer; 318 len = xfr->xfer_len; 319 AT91_TWI_UNLOCK(sc); 320 err = copyin(xfr->xfer_buf, buffer, xfr->xfer_len); 321 AT91_TWI_LOCK(sc); 322 if (err) 323 return (err); 324 /* Setup the xfr for later readback */ 325 xfr->xfer_buf = 0; 326 xfr->xfer_len = 1; 327 while (len--) { 328 WR4(sc, TWI_MMR, TWI_MMR_IADRSZ(xfr->iadrsz) | TWI_MMR_MWRITE | 329 TWI_MMR_DADR(xfr->dadr)); 330 WR4(sc, TWI_IADR, xfr->iadr++); 331 WR4(sc, TWI_THR, *walker++); 332 WR4(sc, TWI_CR, TWI_CR_START); 333 /* 334 * If we get signal while waiting for TXRDY, make sure we 335 * try to stop this device 336 */ 337 while (!(sc->flags & TXRDY)) { 338 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr", 339 0); 340 if (err) 341 break; 342 } 343 WR4(sc, TWI_CR, TWI_CR_STOP); 344 if (err) 345 return (err); 346 while (!(sc->flags & TXCOMP)) { 347 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr2", 348 0); 349 if (err) 350 return (err); 351 } 352 /* Readback */ 353 at91_twi_read_master(sc, xfr); 354 } 355 return (err); 356} 357 358static int 359at91_twi_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 360 struct thread *td) 361{ 362 int err = 0; 363 struct at91_twi_softc *sc; 364 365 sc = CDEV2SOFTC(dev); 366 AT91_TWI_LOCK(sc); 367 while (sc->flags & XFER_PENDING) { 368 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, 369 "twiwait", 0); 370 if (err) { 371 AT91_TWI_UNLOCK(sc); 372 return (err); 373 } 374 } 375 sc->flags |= XFER_PENDING; 376 377 switch (cmd) 378 { 379 case TWIIOCXFER: 380 { 381 struct at91_twi_io *xfr = (struct at91_twi_io *)data; 382 switch (xfr->type) 383 { 384 case TWI_IO_READ_MASTER: 385 err = at91_twi_read_master(sc, xfr); 386 break; 387 case TWI_IO_WRITE_MASTER: 388 err = at91_twi_write_master(sc, xfr); 389 break; 390 default: 391 err = EINVAL; 392 break; 393 } 394 break; 395 } 396 397 case TWIIOCSETCLOCK: 398 { 399 struct at91_twi_clock *twick = (struct at91_twi_clock *)data; 400 401 sc->cwgr = TWI_CWGR_CKDIV(twick->ckdiv) | 402 TWI_CWGR_CHDIV(TWI_CWGR_DIV(twick->high_rate)) | 403 TWI_CWGR_CLDIV(TWI_CWGR_DIV(twick->low_rate)); 404 WR4(sc, TWI_CR, TWI_CR_SWRST); 405 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 406 WR4(sc, TWI_CWGR, sc->cwgr); 407 break; 408 } 409 default: 410 err = ENOTTY; 411 break; 412 } 413 sc->flags &= ~XFER_PENDING; 414 AT91_TWI_UNLOCK(sc); 415 wakeup(sc); 416 return err; 417} 418 419static device_method_t at91_twi_methods[] = { 420 /* Device interface */ 421 DEVMETHOD(device_probe, at91_twi_probe), 422 DEVMETHOD(device_attach, at91_twi_attach), 423 DEVMETHOD(device_detach, at91_twi_detach), 424 425 { 0, 0 } 426}; 427 428static driver_t at91_twi_driver = { 429 "at91_twi", 430 at91_twi_methods, 431 sizeof(struct at91_twi_softc), 432}; 433 434DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, 0, 0); 435