at91_twi.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by EmbeddedIT, 
00003  * Ole Reinhardt <ole.reinhardt@embedded-it.de> All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY EMBEDDED IT AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EMBEDDED IT
00022  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00023  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00024  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00025  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00026  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00027  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00028  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: at91_twi.c,v $
00036  * Revision 1.8  2009/01/17 11:26:37  haraldkipp
00037  * Getting rid of two remaining BSD types in favor of stdint.
00038  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00039  *
00040  * Revision 1.7  2008/10/03 11:31:27  haraldkipp
00041  * Added TWI support for the AT91SAM9260.
00042  *
00043  * Revision 1.6  2008/08/11 06:59:04  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.5  2008/02/15 16:58:41  haraldkipp
00047  * Spport for AT91SAM7SE512 added.
00048  *
00049  * Revision 1.4  2007/12/09 22:17:23  olereinhardt
00050  * fixed typo
00051  *
00052  * Revision 1.3  2007/12/09 21:52:27  olereinhardt
00053  * Added doxygen tags
00054  *
00055  * Revision 1.2  2007/10/04 19:51:56  olereinhardt
00056  * Support for sam7s added
00057  *
00058  * Revision 1.1  2007/09/06 19:36:00  olereinhardt
00059  * First checkin, new twi driver for at91 (currently SAM7X256 is supported
00060  * only)
00061  *
00062  */
00063 
00064 #include <arch/arm.h>
00065 #include <dev/irqreg.h>
00066 
00067 #include <sys/event.h>
00068 #include <sys/atom.h>
00069 #include <sys/timer.h>
00070 #include <sys/thread.h>
00071 #include <sys/heap.h>
00072 
00073 #include <dev/twif.h>
00074 
00079 
00080 HANDLE tw_mm_mutex;                 /* Exclusive master access. */
00081 HANDLE tw_mm_que;                   /* Threads waiting for master transfer done. */
00082 
00083 static uint8_t tw_mm_sla;            /* Destination slave address. */
00084 static volatile uint8_t tw_mm_err;   /* Current master mode error. */
00085 static uint8_t tw_mm_error;          /* Last master mode error. */
00086 
00087 static CONST uint8_t *tw_mt_buf;     /* Pointer to the master transmit buffer. */
00088 static volatile uint16_t tw_mt_len;  /* Number of bytes to transmit in master mode. */
00089 static volatile uint16_t tw_mt_idx;  /* Current master transmit buffer index. */
00090 
00091 static uint8_t *tw_mr_buf;           /* Pointer to the master receive buffer. */
00092 static volatile uint16_t tw_mr_siz;  /* Size of the master receive buffer. */
00093 static volatile uint16_t tw_mr_idx;  /* Current master receive buffer index. */
00094 
00095 #if defined (MCU_AT91SAM7X256) || defined (MCU_AT91SAM7S256) || defined (MCU_AT91SAM7SE512)
00096 
00097 #define TWI_PIO_ASR PIOA_ASR
00098 #define TWI_PIO_PDR PIOA_PDR
00099 #define TWI_PIO_MDER PIOA_MDER
00100 
00101    #if defined (MCU_AT91SAM7X256)
00102       #define TWI_TWD  PA10_TWD_A
00103       #define TWI_TWCK PA11_TWCK_A
00104    #elif defined (MCU_AT91SAM7S256) || defined (MCU_AT91SAM7SE512)
00105       #define TWI_TWD  PA3_TWD_A
00106       #define TWI_TWCK PA4_TWCK_A
00107    #endif
00108 #endif
00109 
00110 #if defined (MCU_AT91SAM9260)
00111 #define TWI_PIO_ASR     PIOA_ASR
00112 #define TWI_PIO_PDR     PIOA_PDR
00113 #define TWI_PIO_MDER    PIOA_MDER
00114 #define TWI_TWD         PA23_TWD_A
00115 #define TWI_TWCK        PA24_TWCK_A
00116 #endif
00117 
00118 /*
00119  * TWI interrupt handler.
00120  */
00121 static void TwInterrupt(void *arg)
00122 {
00123     register unsigned int twsr = inr(TWI_SR) & (TWI_NACK | TWI_RXRDY | TWI_TXRDY | TWI_TXCOMP);;   
00124 
00125     /* Transmission is complete, signal waiting threads */
00126     if (twsr & TWI_TXCOMP) {
00127         outr(TWI_IDR, 0xFFFFFFFF);
00128         NutEventPostFromIrq(&tw_mm_que);
00129     }
00130     
00131     if (twsr & TWI_RXRDY) {
00132         if (tw_mr_idx < tw_mr_siz) {
00133             tw_mr_buf[tw_mr_idx++] = inb(TWI_RHR);
00134             /* The last byte will follow, just set the stop condition */
00135             if (tw_mr_idx == tw_mr_siz - 1) {
00136                 outr(TWI_CR, TWI_STOP);
00137             }
00138                 
00139             if (tw_mr_idx == tw_mr_siz) {
00140                 /* Last byte received. Send stop condition and set IRQs */
00141                 outr(TWI_IDR, TWI_RXRDY);
00142                 outr(TWI_IER, TWI_TXCOMP);
00143             }
00144         } 
00145     }
00146     
00147     if (twsr & TWI_TXRDY) {
00148         if (tw_mt_idx < tw_mt_len) {
00149             outb(TWI_THR, tw_mt_buf[tw_mt_idx++]);
00150             /* Last byte? No bytes to read? So send stop condition else if bytes to read switch to read mode */
00151             if (tw_mt_idx == tw_mt_len) {
00152                 if (tw_mr_siz == 0) {
00153                     outr(TWI_CR, TWI_STOP);
00154                     outr(TWI_IDR, TWI_TXRDY);
00155                     outr(TWI_IER, TWI_TXCOMP);
00156                 } else {
00157                     /* Ok, now switch to read mode and send second start condition */
00158                     outr(TWI_MMR, inb(TWI_MMR) | TWI_MREAD);
00159                     outr(TWI_CR,  TWI_START | (tw_mr_siz == 1) ? TWI_STOP : 0);
00160                     outr(TWI_IDR, TWI_TXRDY);
00161                     outr(TWI_IER, TWI_RXRDY);
00162                 }
00163             }
00164         } 
00165     }
00166     
00167     /* We got a nack, stop transmission and wait for TWI_TXCOMP */
00168     if (twsr & TWI_NACK) {;
00169         /* send stop condition and wake up threads */
00170         outr(TWI_CR, TWI_STOP);
00171         tw_mm_err = TWERR_DATA_NACK;
00172         tw_mt_idx = 0;
00173         tw_mt_len = 0;
00174         tw_mr_siz = 0;
00175         outr(TWI_IDR, 0xFFFFFFFF);
00176         /* Wake up the application. */
00177         NutEventPostFromIrq(&tw_mm_que);
00178     }
00179 }
00180 
00207 int TwMasterTransact(uint8_t sla, CONST void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00208 {
00209     int rc = -1;
00210 
00211     /* This routine is marked reentrant, so lock the interface. */
00212     if(NutEventWait(&tw_mm_mutex, 500)) {
00213         tw_mm_err = TWERR_IF_LOCKED;
00214         NutEventPost(&tw_mm_mutex);
00215         return -1;
00216     }
00217     NutIrqEnable(&sig_TWI);
00218 
00219     NutEnterCritical();
00220     /* Set all parameters for master mode. */
00221     tw_mm_sla = sla;
00222     tw_mm_err = 0;
00223     tw_mt_len = txlen;
00224     tw_mt_idx = 0;
00225     tw_mt_buf = txdata;
00226     tw_mr_siz = rxsiz;
00227     tw_mr_buf = rxdata;
00228     tw_mr_idx = 0;
00229 
00230     if ((tw_mt_len == 0) && (tw_mr_siz == 0)) return -1;
00231 
00232     /* Set slave address enable interrupts and start transmission */
00233     
00234     outr(TWI_MMR, (tw_mm_sla << 16) | (tw_mt_len == 0 ? TWI_MREAD : 0));
00235     
00236     /* Enable interrupts depending on read / write direction and data size */     
00237     if (tw_mt_len == 0) {  
00238         outr(TWI_IDR, TWI_TXRDY | TWI_TXCOMP);
00239         outr(TWI_IER, TWI_RXRDY | TWI_NACK);
00240     } else {
00241         outr(TWI_IDR, TWI_RXRDY);
00242         if ((tw_mt_len == 1) && (tw_mr_siz == 0)) {
00243             outr(TWI_IDR, TWI_TXRDY);
00244             outr(TWI_IER, TWI_TXCOMP);
00245         } else {            
00246             outr(TWI_IER, TWI_TXRDY);
00247             outr(TWI_IDR, TWI_TXCOMP);
00248         }
00249         outr(TWI_IER, TWI_NACK);        
00250     }
00251 
00252     /* Now start transmission if we have any data */
00253     if (tw_mt_len > 0) {
00254         outb(TWI_THR, tw_mt_buf[tw_mt_idx++]);
00255     }     
00256     
00257     /* Send start condition. If read / write only one byte send stop as well */
00258     outr(TWI_CR, TWI_START | (((tw_mt_len == 1) && (tw_mr_siz == 0)) || 
00259                               ((tw_mt_len == 0) && (tw_mr_siz == 1))) ? TWI_STOP : 0);
00260         
00261     NutExitCritical();
00262         
00263     /* Wait for master transmission to be done. */
00264     rc = -1;
00265     if (NutEventWait(&tw_mm_que, tmo)) {
00266         tw_mm_error = TWERR_TIMEOUT;
00267     } else {
00268         NutEnterCritical();
00269         if (tw_mm_err) {
00270             tw_mm_error = tw_mm_err;
00271         } else {
00272             rc = tw_mr_idx;
00273         }
00274         NutExitCritical();
00275     }
00276 
00277     NutIrqDisable(&sig_TWI);
00278     
00279     /* Release the interface. */
00280     NutEventPost(&tw_mm_mutex);
00281     
00282     return rc;
00283 }
00284 
00294 int TwMasterError(void)
00295 {
00296     int rc = (int) tw_mm_error;
00297     tw_mm_error = 0;
00298     return rc;
00299 }
00300 
00316 int TwIOCtl(int req, void *conf)
00317 {
00318     int rc = 0;
00319     unsigned int cldiv, ckdiv;     
00320     unsigned int twi_clk;
00321     switch (req) {
00322 
00323     case TWI_SETSPEED:
00324         ckdiv=1 ;
00325         twi_clk = *((uint32_t *) conf);
00326 
00327         if (twi_clk > 400000) return -1;
00328         
00329         /*
00330          * CLDIV = ((Tlow x 2^CKDIV) -3) x Tmck
00331          * CHDIV = ((THigh x 2^CKDIV) -3) x Tmck
00332          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle) 
00333          */
00334 
00335         while ((cldiv = ((NutGetCpuClock() / (2*twi_clk))-3 ) / (1 << ckdiv)) > 255) {
00336             ckdiv++;
00337         }
00338 
00339         /* BUG 41.2.7.1, datasheet SAM7X256  p. 626 */
00340         if (cldiv * (2 << ckdiv) > 8191) return -1; 
00341         
00342         outr(TWI_CWGR, (ckdiv << 16) | ((unsigned int) cldiv << 8) | (unsigned int) cldiv);
00343         break;
00344 
00345     case TWI_GETSPEED:
00346         ckdiv=1 ;
00347         twi_clk = *((uint32_t *) conf);
00348         
00349         cldiv = inr(TWI_CWGR) & 0x000000FF;
00350         ckdiv = (inr(TWI_CWGR) >> 16) & 0x00000007;
00351             
00352         *((uint32_t *) conf) = NutGetCpuClock() * ((cldiv * 2 << ckdiv) - 3);
00353         break;
00354 
00355     case TWI_GETSTATUS:
00356         break;
00357         
00358     case TWI_SETSTATUS:
00359         break;
00360 
00361     default:
00362         rc = -1;
00363         break;
00364     }
00365     return rc;
00366 }
00367 
00380 int TwInit(uint8_t sla)
00381 {
00382     uint32_t speed = 2400;
00383 
00384     if (NutRegisterIrqHandler(&sig_TWI, TwInterrupt, 0)) {
00385         return -1;
00386     }
00387 
00388     outr(TWI_PIO_ASR, _BV(TWI_TWD) | _BV(TWI_TWCK));  // Set TWD and TWCK as peripheral line
00389     outr(TWI_PIO_PDR, _BV(TWI_TWD) | _BV(TWI_TWCK));  // Let periperal control the PIO lines
00390     
00391     outr(TWI_PIO_MDER, _BV(TWI_TWD) | _BV(TWI_TWCK)); // Enabled OpenDrain output on both lines
00392     
00393     outr(PMC_PCER, _BV(TWI_ID));              // Enable TWI clock in PMC
00394     
00395     outr(TWI_IDR, 0xFFFFFFFF);                // Disable all interrupts 
00396     outr(TWI_CR, TWI_SWRST);                  // Reset bus
00397     outr(TWI_CR, TWI_MSEN | TWI_SVDIS);       // Enable master mode
00398     
00399     TwIOCtl(TWI_SETSPEED, &speed);
00400 
00401     /* Initialize mutex semaphores. */
00402     NutEventPost(&tw_mm_mutex);
00403 
00404     return 0;
00405 }

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