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 2990 2010-04-30 15:17:24Z andreriesberg $
00040  */
00041 
00042 #include <cfg/twi.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 
00058 
00059 /*
00060  * Set ports for known targets.
00061  */
00062 #if defined(MCU_AT91SAM7X)
00063 #define TWI_TWD     PA10_TWD_A
00064 #define TWI_TWCK    PA11_TWCK_A
00065 #elif defined(MCU_AT91SAM7S) || defined(MCU_AT91SAM7SE)
00066 #define TWI_TWD     PA3_TWD_A
00067 #define TWI_TWCK    PA4_TWCK_A
00068 #elif defined (MCU_AT91SAM9260) || defined(MCU_AT91SAM9XE)
00069 #define TWI_TWD     PA23_TWD_A
00070 #define TWI_TWCK    PA24_TWCK_A
00071 #endif
00072 
00073 /*
00074  * Set port defaults, assume PIOA.
00075  */
00076 #ifndef TWI_PIO_ASR
00077 #define TWI_PIO_ASR     PIOA_ASR
00078 #endif
00079 #ifndef TWI_PIO_PDR
00080 #define TWI_PIO_PDR     PIOA_PDR
00081 #endif
00082 #ifndef TWI_PIO_MDER
00083 #define TWI_PIO_MDER    PIOA_MDER
00084 #endif
00085 
00086 static HANDLE tw_mm_mutex;      /* Exclusive master access. */
00087 static HANDLE tw_mm_que;        /* Threads waiting for master transfer done. */
00088 
00089 static volatile int tw_mm_err;  /* Current master mode error. */
00090 static int tw_mm_error;         /* Last master mode error. */
00091 
00092 static uint8_t *tw_mm_buf;      /* Pointer to the master transfer buffer. */
00093 static volatile size_t tw_mm_len;       /* Number of bytes to transmit in master mode. */
00094 static volatile size_t tw_mm_idx;       /* Current master transmit buffer index. */
00095 
00096 /*
00097  * TWI interrupt handler.
00098  */
00099 static void TwInterrupt(void *arg)
00100 {
00101     register unsigned int twsr;
00102 
00103     /* Read the status register and check for errors. */
00104     twsr = inr(TWI_SR);
00105     if (twsr & (TWI_NACK | TWI_OVRE | TWI_ARBLST)) {
00106         if (twsr & TWI_NACK) {
00107             tw_mm_err = TWERR_SLA_NACK;
00108         } else {
00109             tw_mm_err = TWERR_BUS;
00110         }
00111     }
00112 
00113     /* Mask inactive interrupt flags. */
00114     twsr &= inr(TWI_IMR);
00115     if (twsr & TWI_TXRDY) {
00116         /* Byte has been transmitted. */
00117         if (tw_mm_idx < tw_mm_len) {
00118             /* More bytes in buffer, send next one. */
00119             outb(TWI_THR, tw_mm_buf[tw_mm_idx]);
00120             tw_mm_idx++;
00121         } else {
00122             /* All bytes sent, wait for completion. */
00123             outr(TWI_IDR, TWI_TXRDY);
00124             outr(TWI_IER, TWI_TXCOMP);
00125         }
00126     } else if (twsr & TWI_RXRDY) {
00127         /* Byte has been received. */
00128         if (tw_mm_idx < tw_mm_len) {
00129             /* Store byte in receive buffer. */
00130             tw_mm_buf[tw_mm_idx++] = inb(TWI_RHR);
00131             if (tw_mm_idx == tw_mm_len - 1) {
00132                 /* Next byte will be last, set STOP condition. */
00133                 outr(TWI_CR, TWI_STOP);
00134             } else if (tw_mm_idx == tw_mm_len) {
00135                 /* This was the last byte, wait for completion. */
00136                 outr(TWI_IDR, TWI_RXRDY);
00137                 outr(TWI_IER, TWI_TXCOMP);
00138             }
00139         }
00140     } else if (twsr & TWI_TXCOMP) {
00141         /* Transfer is complete, disable interrupts
00142         ** and signal waiting threads */
00143         outr(TWI_IDR, 0xFFFFFFFF);
00144         NutEventPostFromIrq(&tw_mm_que);
00145     }
00146 }
00147 
00173 int TwMasterTransact(uint8_t sla, CONST void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00174 {
00175     int rc = -1;
00176 
00177     /* This routine is marked reentrant, so lock the interface. */
00178     if (NutEventWait(&tw_mm_mutex, tmo)) {
00179         tw_mm_error = TWERR_IF_LOCKED;
00180         return -1;
00181     }
00182 
00183     /* Clear errors. */
00184     tw_mm_err = 0;
00185 
00186     /* Check if there is something to transmit. */
00187     if (txlen) {
00188         /* Set transmit parameters. */
00189         tw_mm_buf = (uint8_t *) txdata;
00190         tw_mm_len = (size_t) txlen;
00191         tw_mm_idx = 1;
00192 
00193         /* Start transmit to specified slave address. */
00194         outr(TWI_MMR, (unsigned int) sla << TWI_DADR_LSB);
00195         outr(TWI_THR, tw_mm_buf[0]);
00196         outr(TWI_IER, TWI_TXRDY);
00197 
00198         /* Wait for transmission complete. */
00199         if (NutEventWait(&tw_mm_que, tmo)) {
00200             tw_mm_err = TWERR_TIMEOUT;
00201         }
00202     }
00203 
00204     if (tw_mm_err == 0) {
00205         /* Reset receive counter. We need this below to set
00206         ** the return value. */
00207         tw_mm_idx = 0;
00208         /* Check if there is something to receive. */
00209         if (rxsiz) {
00210             /* Set remaining receive parameters. */
00211             tw_mm_buf = (uint8_t *) rxdata;
00212             tw_mm_len = (size_t) rxsiz;
00213 
00214             /* Start receive from specified slave address. */
00215             outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | TWI_MREAD);
00216             if (rxsiz == 1) {
00217                 /* Set STOP condition if this is the last byte. */
00218                 outr(TWI_CR, TWI_START | TWI_STOP);
00219             } else {
00220                 outr(TWI_CR, TWI_START);
00221             }
00222             outr(TWI_IER, TWI_RXRDY);
00223 
00224             /* Wait for receiver complete. */
00225             if (NutEventWait(&tw_mm_que, tmo)) {
00226                 tw_mm_err = TWERR_TIMEOUT;
00227             }
00228         }
00229     }
00230     /* Make sure that all interrupts are disabled. */
00231     outr(TWI_IDR, 0xFFFFFFFF);
00232 
00233     /* Check for errors that may have been detected
00234     ** by the interrupt routine. */
00235     if (tw_mm_err) {
00236         tw_mm_error = tw_mm_err;
00237     } else {
00238         rc = (int) tw_mm_idx;
00239     }
00240 
00241     /* Release the interface. */
00242     NutEventPost(&tw_mm_mutex);
00243 
00244     return rc;
00245 }
00246 
00269 int TwMasterRegRead(uint8_t sla, uint32_t iadr, uint8_t iadrlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00270 {
00271     int rc = -1;
00272 
00273     if (rxsiz == 0) {
00274         return -1;
00275     }
00276     /* This routine is marked reentrant, so lock the interface. */
00277     if (NutEventWait(&tw_mm_mutex, tmo)) {
00278         tw_mm_error = TWERR_IF_LOCKED;
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         return -1;
00351     }
00352 
00353     tw_mm_err = 0;
00354 
00355     if (txsiz) {
00356         tw_mm_buf = (uint8_t *) txdata;
00357         tw_mm_len = (size_t) txsiz;
00358         tw_mm_idx = 1;
00359 
00360         /* Set TWI Internal Address Register */
00361         outr(TWI_IADRR, iadr);
00362         /* Set the TWI Master Mode Register */
00363         outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | (((unsigned int) iadrlen & 3) << 8));
00364         outb(TWI_THR, tw_mm_buf[0]);
00365         outr(TWI_IER, TWI_TXRDY);
00366 
00367         /* Wait for master transmission to be done. */
00368         if (NutEventWait(&tw_mm_que, tmo)) {
00369             tw_mm_error = TWERR_TIMEOUT;
00370         } else if (tw_mm_err) {
00371             tw_mm_error = tw_mm_err;
00372         } else {
00373             rc = (int) tw_mm_idx;
00374         }
00375         outr(TWI_IDR, 0xFFFFFFFF);
00376     }
00377 
00378     /* Release the interface. */
00379     NutEventPost(&tw_mm_mutex);
00380 
00381     return rc;
00382 }
00383 
00391 int TwMasterError(void)
00392 {
00393     int rc = tw_mm_error;
00394     tw_mm_error = 0;
00395     return rc;
00396 }
00397 
00407 uint16_t TwMasterIndexes(uint8_t idx)
00408 {
00409     switch (idx) {
00410     case 0:
00411         return (uint16_t) tw_mm_idx;
00412     case 1:
00413         return (uint16_t) tw_mm_idx;
00414     case 2:
00415         return (uint16_t) tw_mm_len;
00416     default:
00417         return 0;
00418     }
00419 }
00420 
00436 int TwIOCtl(int req, void *conf)
00437 {
00438     int rc = 0;
00439     unsigned int cldiv, ckdiv;
00440     unsigned int twi_clk;
00441     switch (req) {
00442 
00443     case TWI_SETSPEED:
00444         ckdiv = 1;
00445         twi_clk = *((uint32_t *) conf);
00446 
00447         if (twi_clk > 400000)
00448             return -1;
00449 
00450         /*
00451          * CLDIV = ((Tlow x 2^CKDIV) -3) x Tmck
00452          * CHDIV = ((THigh x 2^CKDIV) -3) x Tmck
00453          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
00454          */
00455 
00456         while ((cldiv = ((NutClockGet(NUT_HWCLK_PERIPHERAL) / (2 * twi_clk)) - 3) / (1 << ckdiv)) > 255) {
00457             ckdiv++;
00458         }
00459 
00460         /* BUG 41.2.7.1, datasheet SAM7X256  p. 626 */
00461         if (cldiv * (1 << ckdiv) > 8191)
00462             return -1;
00463 
00464         outr(TWI_CWGR, (ckdiv << 16) | ((unsigned int) cldiv << 8) | (unsigned int) cldiv);
00465         break;
00466 
00467     case TWI_GETSPEED:
00468         ckdiv = 1;
00469         twi_clk = *((uint32_t *) conf);
00470 
00471         cldiv = inr(TWI_CWGR) & 0x000000FF;
00472         ckdiv = (inr(TWI_CWGR) >> 16) & 0x00000007;
00473 
00474         *((uint32_t *) conf) = NutGetCpuClock() / ((cldiv * 2 << ckdiv) - 3);
00475         break;
00476 
00477     case TWI_GETSTATUS:
00478         break;
00479 
00480     case TWI_SETSTATUS:
00481         break;
00482 
00483     default:
00484         rc = -1;
00485         break;
00486     }
00487     return rc;
00488 }
00489 
00501 int TwInit(uint8_t sla)
00502 {
00503     uint32_t speed = 4800;
00504 
00505     if (NutRegisterIrqHandler(&sig_TWI, TwInterrupt, 0)) {
00506         return -1;
00507     }
00508 
00509     /* Set TWD and TWCK as peripheral line. */
00510     outr(TWI_PIO_ASR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00511     /* Let periperal control the PIO lines. */
00512     outr(TWI_PIO_PDR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00513     /* Enabled OpenDrain output on both lines. */
00514     outr(TWI_PIO_MDER, _BV(TWI_TWD) | _BV(TWI_TWCK));
00515     /* Enable TWI clock in PMC. */
00516     outr(PMC_PCER, _BV(TWI_ID));
00517 
00518     /* Disable all interrupts. */
00519     outr(TWI_IDR, 0xFFFFFFFF);
00520     /* Reset bus. */
00521     outr(TWI_CR, TWI_SWRST);
00522     /* Enable master mode. */
00523     outr(TWI_CR, TWI_MSEN | TWI_SVDIS);
00524     /* Set initial rate. */
00525     if (TwIOCtl(TWI_SETSPEED, &speed)) {
00526         return -1;
00527     }
00528 
00529     /* Enable level triggered interrupts. */
00530     NutIrqSetMode(&sig_TWI, NUT_IRQMODE_LEVEL);
00531     NutIrqEnable(&sig_TWI);
00532 
00533     /* Initialize mutex semaphores. */
00534     NutEventPost(&tw_mm_mutex);
00535 
00536     return 0;
00537 }
00538 

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