at91_twi.c revision 302408
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include "opt_platform.h" 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sys/arm/at91/at91_twi.c 298055 2016-04-15 14:30:40Z pfg $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/conf.h> 35#include <sys/kernel.h> 36#include <sys/lock.h> 37#include <sys/mbuf.h> 38#include <sys/malloc.h> 39#include <sys/module.h> 40#include <sys/mutex.h> 41#include <sys/rman.h> 42#include <machine/bus.h> 43 44#include <arm/at91/at91_twireg.h> 45#include <arm/at91/at91var.h> 46 47#include <dev/iicbus/iiconf.h> 48#include <dev/iicbus/iicbus.h> 49#include "iicbus_if.h" 50 51#ifdef FDT 52#include <dev/fdt/fdt_common.h> 53#include <dev/ofw/ofw_bus.h> 54#include <dev/ofw/ofw_bus_subr.h> 55#endif 56 57#define TWI_SLOW_CLOCK 1500 58#define TWI_FAST_CLOCK 45000 59#define TWI_FASTEST_CLOCK 90000 60 61struct at91_twi_softc 62{ 63 device_t dev; /* Myself */ 64 void *intrhand; /* Interrupt handle */ 65 struct resource *irq_res; /* IRQ resource */ 66 struct resource *mem_res; /* Memory resource */ 67 struct mtx sc_mtx; /* basically a perimeter lock */ 68 volatile uint32_t flags; 69 uint32_t cwgr; 70 int sc_started; 71 int twi_addr; 72 device_t iicbus; 73}; 74 75static inline uint32_t 76RD4(struct at91_twi_softc *sc, bus_size_t off) 77{ 78 79 return bus_read_4(sc->mem_res, off); 80} 81 82static inline void 83WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val) 84{ 85 86 bus_write_4(sc->mem_res, off, val); 87} 88 89#define AT91_TWI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 90#define AT91_TWI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 91#define AT91_TWI_LOCK_INIT(_sc) \ 92 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 93 "twi", MTX_DEF) 94#define AT91_TWI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 95#define AT91_TWI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 96#define AT91_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 97#define TWI_DEF_CLK 100000 98 99static devclass_t at91_twi_devclass; 100 101/* bus entry points */ 102 103static int at91_twi_probe(device_t dev); 104static int at91_twi_attach(device_t dev); 105static int at91_twi_detach(device_t dev); 106static void at91_twi_intr(void *); 107 108/* helper routines */ 109static int at91_twi_activate(device_t dev); 110static void at91_twi_deactivate(device_t dev); 111 112static int 113at91_twi_probe(device_t dev) 114{ 115#ifdef FDT 116 /* XXXX need a whole list, since there's at least 4 different ones */ 117 if (!ofw_bus_is_compatible(dev, "atmel,at91sam9g20-i2c")) 118 return (ENXIO); 119#endif 120 device_set_desc(dev, "TWI"); 121 return (0); 122} 123 124static int 125at91_twi_attach(device_t dev) 126{ 127 struct at91_twi_softc *sc = device_get_softc(dev); 128 int err; 129 130 sc->dev = dev; 131 err = at91_twi_activate(dev); 132 if (err) 133 goto out; 134 135 AT91_TWI_LOCK_INIT(sc); 136 137#ifdef FDT 138 /* 139 * Disable devices need to hold their resources, so return now and not attach 140 * the iicbus, setup interrupt handlers, etc. 141 */ 142 if (!ofw_bus_status_okay(dev)) 143 return 0; 144#endif 145 146 /* 147 * Activate the interrupt 148 */ 149 err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 150 NULL, at91_twi_intr, sc, &sc->intrhand); 151 if (err) { 152 AT91_TWI_LOCK_DESTROY(sc); 153 goto out; 154 } 155 sc->cwgr = TWI_CWGR_CKDIV(8 * at91_master_clock / TWI_FASTEST_CLOCK) | 156 TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) | 157 TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK)); 158 WR4(sc, TWI_CR, TWI_CR_SWRST); 159 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 160 WR4(sc, TWI_CWGR, sc->cwgr); 161 162 if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) 163 device_printf(dev, "could not allocate iicbus instance\n"); 164 /* probe and attach the iicbus */ 165 bus_generic_attach(dev); 166out: 167 if (err) 168 at91_twi_deactivate(dev); 169 return (err); 170} 171 172static int 173at91_twi_detach(device_t dev) 174{ 175 struct at91_twi_softc *sc; 176 int rv; 177 178 sc = device_get_softc(dev); 179 at91_twi_deactivate(dev); 180 if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0) 181 return (rv); 182 183 AT91_TWI_LOCK_DESTROY(sc); 184 185 return (0); 186} 187 188static int 189at91_twi_activate(device_t dev) 190{ 191 struct at91_twi_softc *sc; 192 int rid; 193 194 sc = device_get_softc(dev); 195 rid = 0; 196 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 197 RF_ACTIVE); 198 if (sc->mem_res == NULL) 199 goto errout; 200 rid = 0; 201 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 202 RF_ACTIVE); 203 if (sc->irq_res == NULL) 204 goto errout; 205 return (0); 206errout: 207 at91_twi_deactivate(dev); 208 return (ENOMEM); 209} 210 211static void 212at91_twi_deactivate(device_t dev) 213{ 214 struct at91_twi_softc *sc; 215 216 sc = device_get_softc(dev); 217 if (sc->intrhand) 218 bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 219 sc->intrhand = NULL; 220 bus_generic_detach(sc->dev); 221 if (sc->mem_res) 222 bus_release_resource(dev, SYS_RES_MEMORY, 223 rman_get_rid(sc->mem_res), sc->mem_res); 224 sc->mem_res = NULL; 225 if (sc->irq_res) 226 bus_release_resource(dev, SYS_RES_IRQ, 227 rman_get_rid(sc->irq_res), sc->irq_res); 228 sc->irq_res = NULL; 229 return; 230} 231 232static void 233at91_twi_intr(void *xsc) 234{ 235 struct at91_twi_softc *sc = xsc; 236 uint32_t status; 237 238 status = RD4(sc, TWI_SR); 239 if (status == 0) 240 return; 241 AT91_TWI_LOCK(sc); 242 sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); 243 if (status & TWI_SR_RXRDY) 244 sc->flags |= TWI_SR_RXRDY; 245 if (status & TWI_SR_TXRDY) 246 sc->flags |= TWI_SR_TXRDY; 247 if (status & TWI_SR_TXCOMP) 248 sc->flags |= TWI_SR_TXCOMP; 249 WR4(sc, TWI_IDR, status); 250 wakeup(sc); 251 AT91_TWI_UNLOCK(sc); 252 return; 253} 254 255static int 256at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit) 257{ 258 int err = 0; 259 int counter = 100000; 260 uint32_t sr; 261 262 AT91_TWI_ASSERT_LOCKED(sc); 263 while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0 && 264 !(sr & TWI_SR_NACK)) 265 continue; 266 if (counter <= 0) 267 err = EBUSY; 268 else if (sr & TWI_SR_NACK) 269 err = ENXIO; // iic nack convention 270 return (err); 271} 272 273static int 274at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 275{ 276 struct at91_twi_softc *sc; 277 int clk; 278 279 sc = device_get_softc(dev); 280 AT91_TWI_LOCK(sc); 281 if (oldaddr) 282 *oldaddr = sc->twi_addr; 283 sc->twi_addr = addr; 284 285 /* 286 * speeds are for 1.5kb/s, 45kb/s and 90kb/s. 287 */ 288 switch (speed) { 289 case IIC_SLOW: 290 clk = TWI_SLOW_CLOCK; 291 break; 292 293 case IIC_FAST: 294 clk = TWI_FAST_CLOCK; 295 break; 296 297 case IIC_UNKNOWN: 298 case IIC_FASTEST: 299 default: 300 clk = TWI_FASTEST_CLOCK; 301 break; 302 } 303 sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) | 304 TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk)); 305 WR4(sc, TWI_CR, TWI_CR_SWRST); 306 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 307 WR4(sc, TWI_CWGR, sc->cwgr); 308 AT91_TWI_UNLOCK(sc); 309 310 return 0; 311} 312 313static int 314at91_twi_callback(device_t dev, int index, caddr_t data) 315{ 316 int error = 0; 317 318 switch (index) { 319 case IIC_REQUEST_BUS: 320 break; 321 322 case IIC_RELEASE_BUS: 323 break; 324 325 default: 326 error = EINVAL; 327 } 328 329 return (error); 330} 331 332static int 333at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 334{ 335 struct at91_twi_softc *sc; 336 int i, len, err; 337 uint32_t rdwr; 338 uint8_t *buf; 339 uint32_t sr; 340 341 sc = device_get_softc(dev); 342 err = 0; 343 AT91_TWI_LOCK(sc); 344 for (i = 0; i < nmsgs; i++) { 345 /* 346 * The linux atmel driver doesn't use the internal device 347 * address feature of twi. A separate i2c message needs to 348 * be written to use this. 349 * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html 350 * for details. Upon reflection, we could use this as an 351 * optimization, but it is unclear the code bloat will 352 * result in faster/better operations. 353 */ 354 rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0; 355 WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr); 356 len = msgs[i].len; 357 buf = msgs[i].buf; 358 /* zero byte transfers aren't allowed */ 359 if (len == 0 || buf == NULL) { 360 err = EINVAL; 361 goto out; 362 } 363 if (len == 1 && msgs[i].flags & IIC_M_RD) 364 WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP); 365 else 366 WR4(sc, TWI_CR, TWI_CR_START); 367 if (msgs[i].flags & IIC_M_RD) { 368 sr = RD4(sc, TWI_SR); 369 while (!(sr & TWI_SR_TXCOMP)) { 370 if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) { 371 len--; 372 *buf++ = RD4(sc, TWI_RHR) & 0xff; 373 if (len == 1) 374 WR4(sc, TWI_CR, TWI_CR_STOP); 375 } 376 } 377 if (len > 0 || (sr & TWI_SR_NACK)) { 378 err = ENXIO; // iic nack convention 379 goto out; 380 } 381 } else { 382 while (len--) { 383 if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) 384 goto out; 385 WR4(sc, TWI_THR, *buf++); 386 } 387 WR4(sc, TWI_CR, TWI_CR_STOP); 388 } 389 if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP))) 390 break; 391 } 392out: 393 if (err) { 394 WR4(sc, TWI_CR, TWI_CR_SWRST); 395 WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); 396 WR4(sc, TWI_CWGR, sc->cwgr); 397 } 398 AT91_TWI_UNLOCK(sc); 399 return (err); 400} 401 402static device_method_t at91_twi_methods[] = { 403 /* Device interface */ 404 DEVMETHOD(device_probe, at91_twi_probe), 405 DEVMETHOD(device_attach, at91_twi_attach), 406 DEVMETHOD(device_detach, at91_twi_detach), 407 408 /* iicbus interface */ 409 DEVMETHOD(iicbus_callback, at91_twi_callback), 410 DEVMETHOD(iicbus_reset, at91_twi_rst_card), 411 DEVMETHOD(iicbus_transfer, at91_twi_transfer), 412 DEVMETHOD_END 413}; 414 415static driver_t at91_twi_driver = { 416 "at91_twi", 417 at91_twi_methods, 418 sizeof(struct at91_twi_softc), 419}; 420 421#ifdef FDT 422DRIVER_MODULE(at91_twi, simplebus, at91_twi_driver, at91_twi_devclass, NULL, 423 NULL); 424#else 425DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, NULL, 426 NULL); 427#endif 428DRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, NULL, NULL); 429MODULE_DEPEND(at91_twi, iicbus, 1, 1, 1); 430