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.1  2007/09/06 19:36:00  olereinhardt
00037  * First checkin, new twi driver for at91 (currently SAM7X256 is supported
00038  * only)
00039  *
00040  */
00041 
00042 #include <stdio.h>
00043 #include <arch/arm.h>
00044 #include <dev/irqreg.h>
00045 
00046 #include <sys/event.h>
00047 #include <sys/atom.h>
00048 #include <sys/timer.h>
00049 #include <sys/thread.h>
00050 #include <sys/heap.h>
00051 
00052 #include <dev/twif.h>
00053 
00054 HANDLE tw_mm_mutex;                 /* Exclusive master access. */
00055 HANDLE tw_mm_que;                   /* Threads waiting for master transfer done. */
00056 
00057 static u_char tw_mm_sla;            /* Destination slave address. */
00058 static volatile u_char tw_mm_err;   /* Current master mode error. */
00059 static u_char tw_mm_error;          /* Last master mode error. */
00060 
00061 static CONST u_char *tw_mt_buf;     /* Pointer to the master transmit buffer. */
00062 static volatile u_short tw_mt_len;  /* Number of bytes to transmit in master mode. */
00063 static volatile u_short tw_mt_idx;  /* Current master transmit buffer index. */
00064 
00065 static u_char *tw_mr_buf;           /* Pointer to the master receive buffer. */
00066 static volatile u_short tw_mr_siz;  /* Size of the master receive buffer. */
00067 static volatile u_short tw_mr_idx;  /* Current master receive buffer index. */
00068 
00069 #if defined (MCU_AT91SAM7X256) 
00070 
00071 #define TWI_PIO_ASR PIOA_ASR
00072 #define TWI_PIO_PDR PIOA_PDR
00073 #define TWI_PIO_MDER PIOA_MDER
00074 
00075 #define TWI_TWD  PA10_TWD_A
00076 #define TWI_TWCK PA11_TWCK_A
00077 
00078 #endif
00079 
00080 /*
00081  * TWI interrupt handler.
00082  */
00083 static void TwInterrupt(void *arg)
00084 {
00085     register u_int twsr = inr(TWI_SR) & (TWI_NACK | TWI_RXRDY | TWI_TXRDY | TWI_TXCOMP);;   
00086 
00087     /* Transmission is complete, signal waiting threads */
00088     if (twsr & TWI_TXCOMP) {
00089         outr(TWI_IDR, 0xFFFFFFFF);
00090         NutEventPostFromIrq(&tw_mm_que);
00091     }
00092     
00093     if (twsr & TWI_RXRDY) {
00094         if (tw_mr_idx < tw_mr_siz) {
00095             tw_mr_buf[tw_mr_idx++] = inb(TWI_RHR);
00096             /* The last byte will follow, just set the stop condition */
00097             if (tw_mr_idx == tw_mr_siz - 1) {
00098                 outr(TWI_CR, TWI_STOP);
00099             }
00100                 
00101             if (tw_mr_idx == tw_mr_siz) {
00102                 /* Last byte received. Send stop condition and set IRQs */
00103                 outr(TWI_IDR, TWI_RXRDY);
00104                 outr(TWI_IER, TWI_TXCOMP);
00105             }
00106         } 
00107     }
00108     
00109     if (twsr & TWI_TXRDY) {
00110         if (tw_mt_idx < tw_mt_len) {
00111             outb(TWI_THR, tw_mt_buf[tw_mt_idx++]);
00112             /* Last byte? No bytes to read? So send stop condition else if bytes to read switch to read mode */
00113             if (tw_mt_idx == tw_mt_len) {
00114                 if (tw_mr_siz == 0) {
00115                     outr(TWI_CR, TWI_STOP);
00116                     outr(TWI_IDR, TWI_TXRDY);
00117                     outr(TWI_IER, TWI_TXCOMP);
00118                 } else {
00119                     /* Ok, now switch to read mode and send second start condition */
00120                     outr(TWI_MMR, inb(TWI_MMR) | TWI_MREAD);
00121                     outr(TWI_CR,  TWI_START | (tw_mr_siz == 1) ? TWI_STOP : 0);
00122                     outr(TWI_IDR, TWI_TXRDY);
00123                     outr(TWI_IER, TWI_RXRDY);
00124                 }
00125             }
00126         } 
00127     }
00128     
00129     /* We got a nack, stop transmission and wait for TWI_TXCOMP */
00130     if (twsr & TWI_NACK) {;
00131         /* send stop condition and wake up threads */
00132         outr(TWI_CR, TWI_STOP);
00133         tw_mm_err = TWERR_DATA_NACK;
00134         tw_mt_idx = 0;
00135         tw_mt_len = 0;
00136         tw_mr_siz = 0;
00137         outr(TWI_IDR, 0xFFFFFFFF);
00138         /* Wake up the application. */
00139         NutEventPostFromIrq(&tw_mm_que);
00140     }
00141 }
00142 
00169 int TwMasterTransact(u_char sla, CONST void *txdata, u_short txlen, void *rxdata, u_short rxsiz, u_long tmo)
00170 {
00171     int rc = -1;
00172 
00173     /* This routine is marked reentrant, so lock the interface. */
00174     if(NutEventWait(&tw_mm_mutex, 500)) {
00175         tw_mm_err = TWERR_IF_LOCKED;
00176         NutEventPost(&tw_mm_mutex);
00177         return -1;
00178     }
00179     NutIrqEnable(&sig_TWI);
00180 
00181     NutEnterCritical();
00182     /* Set all parameters for master mode. */
00183     tw_mm_sla = sla;
00184     tw_mm_err = 0;
00185     tw_mt_len = txlen;
00186     tw_mt_idx = 0;
00187     tw_mt_buf = txdata;
00188     tw_mr_siz = rxsiz;
00189     tw_mr_buf = rxdata;
00190     tw_mr_idx = 0;
00191 
00192     if ((tw_mt_len == 0) && (tw_mr_siz == 0)) return -1;
00193 
00194     /* Set slave address enable interrupts and start transmission */
00195     
00196     outr(TWI_MMR, (tw_mm_sla << 16) | (tw_mt_len == 0 ? TWI_MREAD : 0));
00197     
00198     /* Enable interrupts depending on read / write direction and data size */     
00199     if (tw_mt_len == 0) {  
00200         outr(TWI_IDR, TWI_TXRDY | TWI_TXCOMP);
00201         outr(TWI_IER, TWI_RXRDY | TWI_NACK);
00202     } else {
00203         outr(TWI_IDR, TWI_RXRDY);
00204         if ((tw_mt_len == 1) && (tw_mr_siz == 0)) {
00205             outr(TWI_IDR, TWI_TXRDY);
00206             outr(TWI_IER, TWI_TXCOMP);
00207         } else {            
00208             outr(TWI_IER, TWI_TXRDY);
00209             outr(TWI_IDR, TWI_TXCOMP);
00210         }
00211         outr(TWI_IER, TWI_NACK);        
00212     }
00213 
00214     /* Now start transmission if we have any data */
00215     if (tw_mt_len > 0) {
00216         outb(TWI_THR, tw_mt_buf[tw_mt_idx++]);
00217     }     
00218     
00219     /* Send start condition. If read / write only one byte send stop as well */
00220     outr(TWI_CR, TWI_START | (((tw_mt_len == 1) && (tw_mr_siz == 0)) || 
00221                               ((tw_mt_len == 0) && (tw_mr_siz == 1))) ? TWI_STOP : 0);
00222         
00223     NutExitCritical();
00224         
00225     /* Wait for master transmission to be done. */
00226     rc = -1;
00227     if (NutEventWait(&tw_mm_que, tmo)) {
00228         tw_mm_error = TWERR_TIMEOUT;
00229     } else {
00230         NutEnterCritical();
00231         if (tw_mm_err) {
00232             tw_mm_error = tw_mm_err;
00233         } else {
00234             rc = tw_mr_idx;
00235         }
00236         NutExitCritical();
00237     }
00238 
00239     NutIrqDisable(&sig_TWI);
00240     
00241     /* Release the interface. */
00242     NutEventPost(&tw_mm_mutex);
00243     
00244     return rc;
00245 }
00246 
00256 int TwMasterError(void)
00257 {
00258     int rc = (int) tw_mm_error;
00259     tw_mm_error = 0;
00260     return rc;
00261 }
00262 
00278 int TwIOCtl(int req, void *conf)
00279 {
00280     int rc = 0;
00281     unsigned int cldiv, ckdiv;     
00282     unsigned int twi_clk;
00283     switch (req) {
00284 
00285     case TWI_SETSPEED:
00286         ckdiv=1 ;
00287         twi_clk = *((u_long *) conf);
00288 
00289         if (twi_clk > 400000) return -1;
00290         
00291         /*
00292          * CLDIV = ((Tlow x 2^CKDIV) -3) x Tmck
00293          * CHDIV = ((THigh x 2^CKDIV) -3) x Tmck
00294          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle) 
00295          */
00296 
00297         while ((cldiv = ((NutGetCpuClock() / (2*twi_clk))-3 ) / (1 << ckdiv)) > 255) {
00298             ckdiv++;
00299         }
00300 
00301         /* BUG 41.2.7.1, datasheet SAM7X256  p. 626 */
00302         if (cldiv * (2 << ckdiv) > 8191) return -1; 
00303         
00304         outr(TWI_CWGR, (ckdiv << 16) | ((u_int) cldiv << 8) | (u_int) cldiv);
00305         break;
00306 
00307     case TWI_GETSPEED:
00308         ckdiv=1 ;
00309         twi_clk = *((u_long *) conf);
00310         
00311         cldiv = inr(TWI_CWGR) & 0x000000FF;
00312         ckdiv = (inr(TWI_CWGR) >> 16) & 0x00000007;
00313             
00314         *((u_long *) conf) = NutGetCpuClock() * ((cldiv * 2 << ckdiv) - 3);
00315         break;
00316 
00317     case TWI_GETSTATUS:
00318         break;
00319         
00320     case TWI_SETSTATUS:
00321         break;
00322 
00323     default:
00324         rc = -1;
00325         break;
00326     }
00327     return rc;
00328 }
00329 
00342 int TwInit(u_char sla)
00343 {
00344     u_long speed = 2400;
00345 
00346     if (NutRegisterIrqHandler(&sig_TWI, TwInterrupt, 0)) {
00347         return -1;
00348     }
00349 
00350     outr(TWI_PIO_ASR, _BV(TWI_TWD) | _BV(TWI_TWCK));  // Set TWD and TWCK as peripheral line
00351     outr(TWI_PIO_PDR, _BV(TWI_TWD) | _BV(TWI_TWCK));  // Let periperal control the PIO lines
00352     
00353     outr(TWI_PIO_MDER, _BV(TWI_TWD) | _BV(TWI_TWCK)); // Enabled OpenDrain output on both lines
00354     
00355     outr(PMC_PCER, _BV(TWI_ID));              // Enable TWI clock in PMC
00356     
00357     outr(TWI_IDR, 0xFFFFFFFF);                // Disable all interrupts 
00358     outr(TWI_CR, TWI_SWRST);                  // Reset bus
00359     outr(TWI_CR, TWI_MSEN | TWI_SVDIS);       // Enable master mode
00360     
00361     TwIOCtl(TWI_SETSPEED, &speed);
00362 
00363     /* Initialize mutex semaphores. */
00364     NutEventPost(&tw_mm_mutex);
00365 
00366     return 0;
00367 }

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