Nut/OS  4.10.3
API Reference
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 4234 2012-06-12 09:38:41Z 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 #elif defined (MCU_AT91SAM9G45)
00071 #define TWI_TWD     PA20_TWD0_A
00072 #define TWI_TWCK    PA21_TWCK0_A
00073 #endif
00074 
00075 /*
00076  * Set port defaults, assume PIOA.
00077  */
00078 #ifndef TWI_PIO_ASR
00079 #define TWI_PIO_ASR     PIOA_ASR
00080 #endif
00081 #ifndef TWI_PIO_PDR
00082 #define TWI_PIO_PDR     PIOA_PDR
00083 #endif
00084 #ifndef TWI_PIO_MDER
00085 #define TWI_PIO_MDER    PIOA_MDER
00086 #endif
00087 
00088 static HANDLE tw_mm_mutex;      /* Exclusive master access. */
00089 static HANDLE tw_mm_que;        /* Threads waiting for master transfer done. */
00090 
00091 static volatile int tw_mm_err;  /* Current master mode error. */
00092 static int tw_mm_error;         /* Last master mode error. */
00093 
00094 static uint8_t *tw_mm_buf;      /* Pointer to the master transfer buffer. */
00095 static volatile size_t tw_mm_len;       /* Number of bytes to transmit in master mode. */
00096 static volatile size_t tw_mm_idx;       /* Current master transmit buffer index. */
00097 
00098 /*
00099  * TWI interrupt handler.
00100  */
00101 static void TwInterrupt(void *arg)
00102 {
00103     register unsigned int twsr;
00104 
00105     /* Read the status register and check for errors. */
00106     twsr = inr(TWI_SR);
00107     if (twsr & (TWI_NACK | TWI_OVRE | TWI_ARBLST)) {
00108         if (twsr & TWI_NACK) {
00109             tw_mm_err = TWERR_SLA_NACK;
00110         } else {
00111             tw_mm_err = TWERR_BUS;
00112         }
00113     }
00114 
00115     /* Mask inactive interrupt flags. */
00116     twsr &= inr(TWI_IMR);
00117     if (twsr & TWI_TXRDY) {
00118         /* Byte has been transmitted. */
00119         if (tw_mm_idx < tw_mm_len) {
00120             /* More bytes in buffer, send next one. */
00121             outb(TWI_THR, tw_mm_buf[tw_mm_idx]);
00122             tw_mm_idx++;
00123         } else {
00124             /* All bytes sent, wait for completion. */
00125 #if defined(MCU_AT91SAM9XE)
00126             /* On the SAM9XE the stop condition is not sent automatically
00127                when the transmitter runs out of data. */
00128             outr(TWI_CR, TWI_STOP);
00129 #endif
00130             outr(TWI_IDR, TWI_TXRDY);
00131             outr(TWI_IER, TWI_TXCOMP);
00132         }
00133     } else if (twsr & TWI_RXRDY) {
00134         /* Byte has been received. */
00135         if (tw_mm_idx < tw_mm_len) {
00136             /* Store byte in receive buffer. */
00137             tw_mm_buf[tw_mm_idx++] = inb(TWI_RHR);
00138             if (tw_mm_idx == tw_mm_len - 1) {
00139                 /* Next byte will be last, set STOP condition. */
00140                 outr(TWI_CR, TWI_STOP);
00141             } else if (tw_mm_idx == tw_mm_len) {
00142                 /* This was the last byte, wait for completion. */
00143                 outr(TWI_IDR, TWI_RXRDY);
00144                 outr(TWI_IER, TWI_TXCOMP);
00145             }
00146         }
00147     } else if (twsr & TWI_TXCOMP) {
00148         /* Transfer is complete, disable interrupts
00149         ** and signal waiting threads */
00150         outr(TWI_IDR, 0xFFFFFFFF);
00151         NutEventPostFromIrq(&tw_mm_que);
00152     }
00153 }
00154 
00180 int TwMasterTransact(uint8_t sla, CONST void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00181 {
00182     int rc = -1;
00183 
00184     /* This routine is marked reentrant, so lock the interface. */
00185     if (NutEventWait(&tw_mm_mutex, tmo)) {
00186         tw_mm_error = TWERR_IF_LOCKED;
00187         return -1;
00188     }
00189 
00190     /* Clear errors. */
00191     tw_mm_err = 0;
00192 
00193     /* Check if there is something to transmit. */
00194     if (txlen) {
00195         /* Set transmit parameters. */
00196         tw_mm_buf = (uint8_t *) txdata;
00197         tw_mm_len = (size_t) txlen;
00198         tw_mm_idx = 1;
00199 
00200         /* Start transmit to specified slave address. */
00201         outr(TWI_MMR, (unsigned int) sla << TWI_DADR_LSB);
00202         outr(TWI_THR, tw_mm_buf[0]);
00203         outr(TWI_IER, TWI_TXRDY);
00204 
00205         /* Wait for transmission complete. */
00206         if (NutEventWait(&tw_mm_que, tmo)) {
00207             tw_mm_err = TWERR_TIMEOUT;
00208         }
00209     }
00210 
00211     if (tw_mm_err == 0) {
00212         /* Reset receive counter. We need this below to set
00213         ** the return value. */
00214         tw_mm_idx = 0;
00215         /* Check if there is something to receive. */
00216         if (rxsiz) {
00217             /* Set remaining receive parameters. */
00218             tw_mm_buf = (uint8_t *) rxdata;
00219             tw_mm_len = (size_t) rxsiz;
00220 
00221             /* Start receive from specified slave address. */
00222             outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | TWI_MREAD);
00223             if (rxsiz == 1) {
00224                 /* Set STOP condition if this is the last byte. */
00225                 outr(TWI_CR, TWI_START | TWI_STOP);
00226             } else {
00227                 outr(TWI_CR, TWI_START);
00228             }
00229             outr(TWI_IER, TWI_RXRDY);
00230 
00231             /* Wait for receiver complete. */
00232             if (NutEventWait(&tw_mm_que, tmo)) {
00233                 tw_mm_err = TWERR_TIMEOUT;
00234             }
00235         }
00236     }
00237     /* Make sure that all interrupts are disabled. */
00238     outr(TWI_IDR, 0xFFFFFFFF);
00239 
00240     /* Check for errors that may have been detected
00241     ** by the interrupt routine. */
00242     if (tw_mm_err) {
00243         tw_mm_error = tw_mm_err;
00244     } else {
00245         rc = (int) tw_mm_idx;
00246     }
00247 
00248     /* Release the interface. */
00249     NutEventPost(&tw_mm_mutex);
00250 
00251     return rc;
00252 }
00253 
00276 int TwMasterRegRead(uint8_t sla, uint32_t iadr, uint8_t iadrlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00277 {
00278     int rc = -1;
00279 
00280     if (rxsiz == 0) {
00281         return -1;
00282     }
00283     /* This routine is marked reentrant, so lock the interface. */
00284     if (NutEventWait(&tw_mm_mutex, tmo)) {
00285         tw_mm_error = TWERR_IF_LOCKED;
00286         return -1;
00287     }
00288 
00289     tw_mm_err = 0;
00290 
00291     if (rxsiz) {
00292         tw_mm_buf = rxdata;
00293         tw_mm_len = (size_t) rxsiz;
00294         tw_mm_idx = 0;
00295 
00296         /* Set TWI Internal Address Register if needed */
00297         outr(TWI_IADRR, iadr);
00298         /* Set the TWI Master Mode Register */
00299         outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | (((unsigned int) iadrlen & 3) << 8) | TWI_MREAD);
00300         /* Send start condition. If read only one byte send stop as well */
00301         if (rxsiz == 1) {
00302             outr(TWI_CR, TWI_START | TWI_STOP);
00303         } else {
00304             outr(TWI_CR, TWI_START);
00305         }
00306         outr(TWI_IER, TWI_RXRDY);
00307 
00308         /* Wait for master transmission to be done. */
00309         if (NutEventWait(&tw_mm_que, tmo)) {
00310             tw_mm_error = TWERR_TIMEOUT;
00311         } else if (tw_mm_err) {
00312             tw_mm_error = tw_mm_err;
00313         } else {
00314             rc = (int) tw_mm_idx;
00315         }
00316         outr(TWI_IDR, 0xFFFFFFFF);
00317     }
00318 
00319     /* Release the interface. */
00320     NutEventPost(&tw_mm_mutex);
00321 
00322     return rc;
00323 }
00324 
00350 int TwMasterRegWrite(uint8_t sla, uint32_t iadr, uint8_t iadrlen, CONST void *txdata, uint16_t txsiz, uint32_t tmo)
00351 {
00352     int rc = -1;
00353 
00354     /* This routine is marked reentrant, so lock the interface. */
00355     if (NutEventWait(&tw_mm_mutex, tmo)) {
00356         tw_mm_err = TWERR_IF_LOCKED;
00357         return -1;
00358     }
00359 
00360     tw_mm_err = 0;
00361 
00362     if (txsiz) {
00363         tw_mm_buf = (uint8_t *) txdata;
00364         tw_mm_len = (size_t) txsiz;
00365         tw_mm_idx = 1;
00366 
00367         /* Set TWI Internal Address Register */
00368         outr(TWI_IADRR, iadr);
00369         /* Set the TWI Master Mode Register */
00370         outr(TWI_MMR, ((unsigned int) sla << TWI_DADR_LSB) | (((unsigned int) iadrlen & 3) << 8));
00371         outb(TWI_THR, tw_mm_buf[0]);
00372         outr(TWI_IER, TWI_TXRDY);
00373 
00374         /* Wait for master transmission to be done. */
00375         if (NutEventWait(&tw_mm_que, tmo)) {
00376             tw_mm_error = TWERR_TIMEOUT;
00377         } else if (tw_mm_err) {
00378             tw_mm_error = tw_mm_err;
00379         } else {
00380             rc = (int) tw_mm_idx;
00381         }
00382         outr(TWI_IDR, 0xFFFFFFFF);
00383     }
00384 
00385     /* Release the interface. */
00386     NutEventPost(&tw_mm_mutex);
00387 
00388     return rc;
00389 }
00390 
00398 int TwMasterError(void)
00399 {
00400     int rc = tw_mm_error;
00401     tw_mm_error = 0;
00402     return rc;
00403 }
00404 
00414 uint16_t TwMasterIndexes(uint8_t idx)
00415 {
00416     switch (idx) {
00417     case 0:
00418         return (uint16_t) tw_mm_idx;
00419     case 1:
00420         return (uint16_t) tw_mm_idx;
00421     case 2:
00422         return (uint16_t) tw_mm_len;
00423     default:
00424         return 0;
00425     }
00426 }
00427 
00443 int TwIOCtl(int req, void *conf)
00444 {
00445     int rc = 0;
00446     unsigned int cldiv, ckdiv;
00447     unsigned int twi_clk;
00448     switch (req) {
00449 
00450     case TWI_SETSPEED:
00451         ckdiv = 1;
00452         twi_clk = *((uint32_t *) conf);
00453 
00454         if (twi_clk > 400000)
00455             return -1;
00456 
00457         /*
00458          * CLDIV = ((Tlow x 2^CKDIV) -3) x Tmck
00459          * CHDIV = ((THigh x 2^CKDIV) -3) x Tmck
00460          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
00461          */
00462 
00463         while ((cldiv = ((NutClockGet(NUT_HWCLK_PERIPHERAL) / (2 * twi_clk)) - 3) / (1 << ckdiv)) > 255) {
00464             ckdiv++;
00465         }
00466 
00467         /* BUG 41.2.7.1, datasheet SAM7X256  p. 626 */
00468         if (cldiv * (1 << ckdiv) > 8191)
00469             return -1;
00470 
00471         outr(TWI_CWGR, (ckdiv << 16) | ((unsigned int) cldiv << 8) | (unsigned int) cldiv);
00472         break;
00473 
00474     case TWI_GETSPEED:
00475         ckdiv = 1;
00476         twi_clk = *((uint32_t *) conf);
00477 
00478         cldiv = inr(TWI_CWGR) & 0x000000FF;
00479         ckdiv = (inr(TWI_CWGR) >> 16) & 0x00000007;
00480 
00481         *((uint32_t *) conf) = NutGetCpuClock() / ((cldiv * 2 << ckdiv) - 3);
00482         break;
00483 
00484     case TWI_GETSTATUS:
00485         break;
00486 
00487     case TWI_SETSTATUS:
00488         break;
00489 
00490     default:
00491         rc = -1;
00492         break;
00493     }
00494     return rc;
00495 }
00496 
00508 int TwInit(uint8_t sla)
00509 {
00510     uint32_t speed = 4800;
00511 
00512     if (NutRegisterIrqHandler(&sig_TWI, TwInterrupt, 0)) {
00513         return -1;
00514     }
00515 
00516     /* Set TWD and TWCK as peripheral line. */
00517     outr(TWI_PIO_ASR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00518     /* Let periperal control the PIO lines. */
00519     outr(TWI_PIO_PDR, _BV(TWI_TWD) | _BV(TWI_TWCK));
00520     /* Enabled OpenDrain output on both lines. */
00521     outr(TWI_PIO_MDER, _BV(TWI_TWD) | _BV(TWI_TWCK));
00522     /* Enable TWI clock in PMC. */
00523     outr(PMC_PCER, _BV(TWI_ID));
00524 
00525     /* Disable all interrupts. */
00526     outr(TWI_IDR, 0xFFFFFFFF);
00527     /* Reset bus. */
00528     outr(TWI_CR, TWI_SWRST);
00529     /* Enable master mode. */
00530     outr(TWI_CR, TWI_MSEN | TWI_SVDIS);
00531     /* Set initial rate. */
00532     if (TwIOCtl(TWI_SETSPEED, &speed)) {
00533         return -1;
00534     }
00535 
00536     /* Enable level triggered interrupts. */
00537     NutIrqSetMode(&sig_TWI, NUT_IRQMODE_LEVEL);
00538     NutIrqEnable(&sig_TWI);
00539 
00540     /* Initialize mutex semaphores. */
00541     NutEventPost(&tw_mm_mutex);
00542 
00543     return 0;
00544 }
00545