The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/at91/at91_twi.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/6.2/sys/arm/at91/at91_twi.c 160484 2006-07-18 22:00:35Z 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 
   48 struct 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         volatile 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 
   64 static inline uint32_t
   65 RD4(struct at91_twi_softc *sc, bus_size_t off)
   66 {
   67         return bus_read_4(sc->mem_res, off);
   68 }
   69 
   70 static inline void
   71 WR4(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 
   86 static devclass_t at91_twi_devclass;
   87 
   88 /* bus entry points */
   89 
   90 static int at91_twi_probe(device_t dev);
   91 static int at91_twi_attach(device_t dev);
   92 static int at91_twi_detach(device_t dev);
   93 static void at91_twi_intr(void *);
   94 
   95 /* helper routines */
   96 static int at91_twi_activate(device_t dev);
   97 static void at91_twi_deactivate(device_t dev);
   98 
   99 static int
  100 at91_twi_probe(device_t dev)
  101 {
  102         device_set_desc(dev, "TWI");
  103         return (0);
  104 }
  105 
  106 static int
  107 at91_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 
  145 out:;
  146         if (err)
  147                 at91_twi_deactivate(dev);
  148         return (err);
  149 }
  150 
  151 static int
  152 at91_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 
  165 static int
  166 at91_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);
  183 errout:
  184         at91_twi_deactivate(dev);
  185         return (ENOMEM);
  186 }
  187 
  188 static void
  189 at91_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 
  209 static void
  210 at91_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 
  230 static int
  231 at91_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  */
  246 static int
  247 at91_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  */
  264 static int
  265 at91_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         return (0);
  272 }
  273 
  274 /*
  275  * enter a START condition from an idle state.
  276  */
  277 static int
  278 at91_twi_start(device_t dev, u_char slave, int timeout)
  279 {
  280         struct at91_twi_softc *sc;
  281 
  282         sc = device_get_softc(dev);
  283         WR4(sc, TWI_MMR, TWI_MMR_DADR(slave));
  284         sc->sc_started = 1;
  285         return (0);
  286 }
  287 
  288 static int
  289 at91_twi_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */)
  290 {
  291         struct at91_twi_softc *sc;
  292         uint8_t *walker;
  293         int err = 0;
  294 
  295         walker = buf;
  296         sc = device_get_softc(dev);
  297         AT91_TWI_LOCK(sc);
  298         WR4(sc, TWI_MMR, ~TWI_MMR_MREAD & RD4(sc, TWI_MMR));
  299         WR4(sc, TWI_CR, TWI_CR_START);
  300         sc->sc_started = 1;
  301         WR4(sc, TWI_IER, TWI_SR_TXRDY);
  302         while (len--) {
  303                 WR4(sc, TWI_THR, *walker++);
  304                 while (!(sc->flags & TXRDY)) {
  305                         err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr",
  306                             0);
  307                         if (err)
  308                                 goto errout;
  309                 }
  310         }
  311 errout:;
  312         WR4(sc, TWI_IDR, TWI_SR_TXRDY);
  313         AT91_TWI_UNLOCK(sc);
  314         return (err);
  315 }
  316 
  317 static int
  318 at91_twi_read(device_t dev, char *buf, int len, int *read, int last,
  319          int delay /* us */)
  320 {
  321         struct at91_twi_softc *sc;
  322         char *walker;
  323         int err = 0;
  324 
  325         walker = buf;
  326         sc = device_get_softc(dev);
  327         AT91_TWI_LOCK(sc);
  328         WR4(sc, TWI_MMR, TWI_MMR_MREAD | RD4(sc, TWI_MMR));
  329         WR4(sc, TWI_CR, TWI_CR_START);
  330         sc->sc_started = 1;
  331         WR4(sc, TWI_IER, TWI_SR_RXRDY);
  332         while (len-- > 0) {
  333                 err = 0;
  334                 while (!(sc->flags & RXRDY)) {
  335                         err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird",
  336                             0);
  337                         if (err)
  338                                 goto errout;
  339                 }
  340                 sc->flags &= ~RXRDY;
  341                 *walker++ = RD4(sc, TWI_RHR) & 0xff;
  342                 if (len == 1 && last)
  343                         break;
  344         }
  345         if (!last)
  346                 goto errout;
  347         WR4(sc, TWI_CR, TWI_CR_STOP);
  348         err = at91_twi_wait_stop_done(sc);
  349         *walker = RD4(sc, TWI_RHR) & 0xff;
  350         if (read)
  351                 *read = walker - buf;
  352 errout:;
  353         WR4(sc, TWI_IDR, TWI_SR_RXRDY);
  354         AT91_TWI_UNLOCK(sc);
  355         return (err);
  356 }
  357 
  358 static int
  359 at91_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 
  403 static int
  404 at91_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 
  422 static 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 
  439 static driver_t at91_twi_driver = {
  440         "at91_twi",
  441         at91_twi_methods,
  442         sizeof(struct at91_twi_softc),
  443 };
  444 
  445 DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, 0, 0);
  446 DRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, 0, 0);

Cache object: 74511ca3e04744f0855eb7f85d5c1bfa


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.