at91_twi.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by EmbeddedIT, Ole Reinhardt
00003  * Copyright (C) 2009 by Rittal GmbH & Co. KG, Ulrich Prinz
00004  * Copyright 2009 by egnite GmbH
00005  *
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  *
00012  * 1. Redistributions of source code must retain the above copyright
00013  *    notice, this list of conditions and the following disclaimer.
00014  * 2. Redistributions in binary form must reproduce the above copyright
00015  *    notice, this list of conditions and the following disclaimer in the
00016  *    documentation and/or other materials provided with the distribution.
00017  * 3. Neither the name of the copyright holders nor the names of
00018  *    contributors may be used to endorse or promote products derived
00019  *    from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00028  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00029  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00030  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00031  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  *
00034  * For additional information see http://www.ethernut.de/
00035  *
00036  */
00037 
00038 /*
00039  * $Id: at91_twi.c 2620 2009-06-03 09:26:15Z haraldkipp $
00040  */
00041 
00042 #include <arch/arm.h>
00043 #include <dev/irqreg.h>
00044 
00045 #include <sys/event.h>
00046 #include <sys/atom.h>
00047 #include <sys/timer.h>
00048 #include <sys/thread.h>
00049 #include <sys/heap.h>
00050 
00051 #include <dev/twif.h>
00052 
00057 
00058 /*
00059  * Set ports for known targets.
00060  */
00061 #if defined(MCU_AT91SAM7X)
00062 #define TWI_TWD     PA10_TWD_A
00063 #define TWI_TWCK    PA11_TWCK_A
00064 #elif defined(MCU_AT91SAM7S) || defined(MCU_AT91SAM7SE)
00065 #define TWI_TWD     PA3_TWD_A
00066 #define TWI_TWCK    PA4_TWCK_A
00067 #elif defined (MCU_AT91SAM9260) || defined(MCU_AT91SAM9XE)
00068 #define TWI_TWD     PA23_TWD_A
00069 #define TWI_TWCK    PA24_TWCK_A
00070 #endif
00071 
00072 /*
00073  * Set port defaults, assume PIOA.
00074  */
00075 #ifndef TWI_PIO_ASR
00076 #define TWI_PIO_ASR     PIOA_ASR
00077 #endif
00078 #ifndef TWI_PIO_PDR
00079 #define TWI_PIO_PDR     PIOA_PDR
00080 #endif
00081 #ifndef TWI_PIO_MDER
00082 #define TWI_PIO_MDER    PIOA_MDER
00083 #endif
00084 
00085 static HANDLE tw_mm_mutex;      /* Exclusive master access. */
00086 static HANDLE tw_mm_que;        /* Threads waiting for master transfer done. */
00087 
00088 static volatile int tw_mm_err;  /* Current master mode error. */
00089 static int tw_mm_error;         /* Last master mode error. */
00090 
00091 static uint8_t *tw_mm_buf;      /* Pointer to the master transfer buffer. */
00092 static volatile size_t tw_mm_len;       /* Number of bytes to transmit in master mode. */
00093 static volatile size_t tw_mm_idx;       /* Current master transmit buffer index. */
00094 
00095 /*
00096  * TWI interrupt handler.
00097  */
00098 static void TwInterrupt(void *arg)
00099 {
00100     register unsigned int twsr;
00101 
00102     /* Read the status register and check for errors. */
00103     twsr = inr(TWI_SR);
00104     if (twsr & (TWI_NACK | TWI_OVRE | TWI_ARBLST)) {
00105         if (twsr & TWI_NACK) {
00106             tw_mm_err = TWERR_SLA_NACK;
00107         } else {
00108             tw_mm_err = TWERR_BUS;
00109         }
00110     }
00111 
00112     /* Mask inactive interrupt flags. */
00113     twsr &= inr(TWI_IMR);
00114     if (twsr & TWI_TXRDY) {
00115         /* Byte has been transmitted. */
00116         if (tw_mm_idx < tw_mm_len) {
00117             /* More bytes in buffer, send next one. */
00118             outb(TWI_THR, tw_mm_buf[tw_mm_idx]);
00119             tw_mm_idx++;
00120         } else {
00121             /* All bytes sent, wait for completion. */
00122             outr(TWI_IDR, TWI_TXRDY);
00123             outr(TWI_IER, TWI_TXCOMP);
00124         }
00125     } else if (twsr & TWI_RXRDY) {
00126         /* Byte has been received. */
00127         if (tw_mm_idx < tw_mm_len) {
00128             /* Store byte in receive buffer. */
00129             tw_mm_buf[tw_mm_idx++] = inb(TWI_RHR);
00130             if (tw_mm_idx == tw_mm_len - 1) {
00131                 /* Next byte will be last, set STOP condition. */
00132                 outr(TWI_CR, TWI_STOP);
00133             } else if (tw_mm_idx == tw_mm_len) {
00134                 /* This was the last byte, wait for completion. */
00135                 outr(TWI_IDR, TWI_RXRDY);
00136                 outr(TWI_IER, TWI_TXCOMP);
00137             }
00138         }
00139     } else if (twsr & TWI_TXCOMP) {
00140         /* Transfer is complete, disable interrupts 
00141         ** and signal waiting threads */
00142         outr(TWI_IDR, 0xFFFFFFFF);
00143         NutEventPostFromIrq(&tw_mm_que);
00144     }
00145 }
00146 
00172 int TwMasterTransact(uint8_t sla, CONST void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00173 {
00174     int rc = -1;
00175 
00176     /* This routine is marked reentrant, so lock the interface. */
00177     if (NutEventWait(&tw_mm_mutex, tmo)) {
00178         tw_mm_error = TWERR_IF_LOCKED;
00179         return -1;
00180     }
00181 
00182     /* Clear errors. */
00183     tw_mm_err = 0;
00184 
00185     /* Check if there is something to transmit. */
00186     if (txlen) {
00187         /* Set transmit parameters. */
00188         tw_mm_buf = (uint8_t *) txdata;
00189         tw_mm_len = (size_t) txlen;
00190         tw_mm_idx = 1;
00191 
00192         /* Start transmit to specified slave address. */
00193         outr(TWI_MMR, (unsigned int) sla << TWI_DADR_LSB);
00194         outr(TWI_THR, tw_mm_buf[0]);
00195         outr(TWI_IER, TWI_TXRDY);
00196 
00197         /* Wait for transmission complete. */
00198         if (NutEventWait(&tw_mm_que, tmo)) {
00199             tw_mm_err = TWERR_TIMEOUT;
00200         }
00201     }
00202 
00203     if (tw_mm_err == 0) {
00204         /* Reset receive counter. We need this below to set
00205         ** the return value. */
00206         tw_mm_idx = 0;
00207         /* Check if there is something to receive. */
00208         if (rxsiz) {
00209             /* Set remaining receive parameters. */
00210             tw_mm_buf = (uint8_t *) rxdata;
00211             tw_mm_len = (size_t) rxsiz;
00212 
00213             /* Start receive from specified slave address. */
00214             outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | TWI_MREAD);
00215             if (rxsiz == 1) {
00216                 /* Set STOP condition if this is the last byte. */
00217                 outr(TWI_CR, TWI_START | TWI_STOP);
00218             } else {
00219                 outr(TWI_CR, TWI_START);
00220             }
00221             outr(TWI_IER, TWI_RXRDY);
00222 
00223             /* Wait for receiver complete. */
00224             if (NutEventWait(&tw_mm_que, tmo)) {
00225                 tw_mm_err = TWERR_TIMEOUT;
00226             }
00227         }
00228     }
00229     /* Make sure that all interrupts are disabled. */
00230     outr(TWI_IDR, 0xFFFFFFFF);
00231 
00232     /* Check for errors that may have been detected 
00233     ** by the interrupt routine. */
00234     if (tw_mm_err) {
00235         tw_mm_error = tw_mm_err;
00236     } else {
00237         rc = (int) tw_mm_idx;
00238     }
00239 
00240     /* Release the interface. */
00241     NutEventPost(&tw_mm_mutex);
00242 
00243     return rc;
00244 }
00245 
00268 int TwMasterRegRead(uint8_t sla, uint32_t iadr, uint8_t iadrlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00269 {
00270     int rc = -1;
00271 
00272     if (rxsiz == 0) {
00273         return -1;
00274     }
00275     /* This routine is marked reentrant, so lock the interface. */
00276     if (NutEventWait(&tw_mm_mutex, tmo)) {
00277         tw_mm_error = TWERR_IF_LOCKED;
00278         NutEventPost(&tw_mm_mutex);
00279         return -1;
00280     }
00281 
00282     tw_mm_err = 0;
00283 
00284     if (rxsiz) {
00285         tw_mm_buf = rxdata;
00286         tw_mm_len = (size_t) rxsiz;
00287         tw_mm_idx = 0;
00288 
00289         /* Set TWI Internal Address Register if needed */
00290         outr(TWI_IADRR, iadr);
00291         /* Set the TWI Master Mode Register */
00292         outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | (((unsigned int) iadrlen & 3) << 8) | TWI_MREAD);
00293         /* Send start condition. If read only one byte send stop as well */
00294         if (rxsiz == 1) {
00295             outr(TWI_CR, TWI_START | TWI_STOP);
00296         } else {
00297             outr(TWI_CR, TWI_START);
00298         }
00299         outr(TWI_IER, TWI_RXRDY);
00300 
00301         /* Wait for master transmission to be done. */
00302         if (NutEventWait(&tw_mm_que, tmo)) {
00303             tw_mm_error = TWERR_TIMEOUT;
00304         } else if (tw_mm_err) {
00305             tw_mm_error = tw_mm_err;
00306         } else {
00307             rc = (int) tw_mm_idx;
00308         }
00309         outr(TWI_IDR, 0xFFFFFFFF);
00310     }
00311 
00312     /* Release the interface. */
00313     NutEventPost(&tw_mm_mutex);
00314 
00315     return rc;
00316 }
00317 
00343 int TwMasterRegWrite(uint8_t sla, uint32_t iadr, uint8_t iadrlen, CONST void *txdata, uint16_t txsiz, uint32_t tmo)
00344 {
00345     int rc = -1;
00346 
00347     /* This routine is marked reentrant, so lock the interface. */
00348     if (NutEventWait(&tw_mm_mutex, tmo)) {
00349         tw_mm_err = TWERR_IF_LOCKED;
00350         NutEventPost(&tw_mm_mutex);
00351         return -1;
00352     }
00353 
00354     tw_mm_err = 0;
00355 
00356     if (txsiz) {
00357         tw_mm_buf = (uint8_t *) txdata;
00358         tw_mm_len = (size_t) txsiz;
00359         tw_mm_idx = 1;
00360 
00361         /* Set TWI Internal Address Register */
00362         outr(TWI_IADRR, iadr);
00363         /* Set the TWI Master Mode Register */
00364         outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | (((unsigned int) iadrlen & 3) << 8));
00365         outb(TWI_THR, tw_mm_buf[0]);
00366         outr(TWI_IER, TWI_TXRDY);
00367 
00368         /* Wait for master transmission to be done. */
00369         if (NutEventWait(&tw_mm_que, tmo)) {
00370             tw_mm_error = TWERR_TIMEOUT;
00371         } else if (tw_mm_err) {
00372             tw_mm_error = tw_mm_err;
00373         } else {
00374             rc = (int) tw_mm_idx;
00375         }
00376         outr(TWI_IDR, 0xFFFFFFFF);
00377     }
00378 
00379     /* Release the interface. */
00380     NutEventPost(&tw_mm_mutex);
00381 
00382     return rc;
00383 }
00384 
00392 int TwMasterError(void)
00393 {
00394     int rc = tw_mm_error;
00395     tw_mm_error = 0;
00396     return rc;
00397 }
00398 
00408 uint16_t TwMasterIndexes(uint8_t idx)
00409 {
00410     switch (idx) {
00411     case 0:
00412         return (uint16_t) tw_mm_idx;
00413     case 1:
00414         return (uint16_t) tw_mm_idx;
00415     case 2:
00416         return (uint16_t) tw_mm_len;
00417     default:
00418         return 0;
00419     }
00420 }
00421 
00437 int TwIOCtl(int req, void *conf)
00438 {
00439     int rc = 0;
00440     unsigned int cldiv, ckdiv;
00441     unsigned int twi_clk;
00442     switch (req) {
00443 
00444     case TWI_SETSPEED:
00445         ckdiv = 1;
00446         twi_clk = *((uint32_t *) conf);
00447 
00448         if (twi_clk > 400000)
00449             return -1;
00450 
00451         /*
00452          * CLDIV = ((Tlow x 2^CKDIV) -3) x Tmck
00453          * CHDIV = ((THigh x 2^CKDIV) -3) x Tmck
00454          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
00455          */
00456 
00457         while ((cldiv = ((NutClockGet(NUT_HWCLK_PERIPHERAL) / (2 * twi_clk)) - 3) / (1 << ckdiv)) > 255) {
00458             ckdiv++;
00459         }
00460 
00461         /* BUG 41.2.7.1, datasheet SAM7X256  p. 626 */
00462         if (cldiv * (1 << ckdiv) > 8191)
00463             return -1;
00464 
00465         outr(TWI_CWGR, (ckdiv << 16) | ((unsigned int) cldiv << 8) | (unsigned int) cldiv);
00466         break;
00467 
00468     case TWI_GETSPEED:
00469         ckdiv = 1;
00470         twi_clk = *((uint32_t *) conf);
00471 
00472         cldiv = inr(TWI_CWGR) & 0x000000FF;
00473         ckdiv = (inr(TWI_CWGR) >> 16) & 0x00000007;
00474 
00475         *((uint32_t *) conf) = NutGetCpuClock() * ((cldiv * 2 << ckdiv) - 3);
00476         break;
00477 
00478     case TWI_GETSTATUS:
00479         break;
00480 
00481     case TWI_SETSTATUS:
00482         break;
00483 
00484     default:
00485         rc = -1;
00486         break;
00487     }
00488     return rc;
00489 }
00490 
00502 int TwInit(uint8_t sla)
00503 {
00504     uint32_t speed = 4800;
00505 
00506     if (NutRegisterIrqHandler(&sig_TWI, TwInterrupt, 0)) {
00507         return -1;
00508     }
00509 
00510     /* Set TWD and TWCK as peripheral line. */
00511     outr(TWI_PIO_ASR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00512     /* Let periperal control the PIO lines. */
00513     outr(TWI_PIO_PDR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00514     /* Enabled OpenDrain output on both lines. */
00515     outr(TWI_PIO_MDER, _BV(TWI_TWD) | _BV(TWI_TWCK));   
00516     /* Enable TWI clock in PMC. */
00517     outr(PMC_PCER, _BV(TWI_ID));        
00518 
00519     /* Disable all interrupts. */
00520     outr(TWI_IDR, 0xFFFFFFFF);
00521     /* Reset bus. */
00522     outr(TWI_CR, TWI_SWRST);
00523     /* Enable master mode. */
00524     outr(TWI_CR, TWI_MSEN | TWI_SVDIS);
00525     /* Set initial rate. */
00526     if (TwIOCtl(TWI_SETSPEED, &speed)) {
00527         return -1;
00528     }
00529 
00530     /* Enable level triggered interrupts. */
00531     NutIrqSetMode(&sig_TWI, NUT_IRQMODE_LEVEL);
00532     NutIrqEnable(&sig_TWI);
00533 
00534     /* Initialize mutex semaphores. */
00535     NutEventPost(&tw_mm_mutex);
00536 
00537     return 0;
00538 }
00539 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/