summaryrefslogblamecommitdiffstats
path: root/bsps/arm/lpc24xx/i2c/i2c.c
blob: 5a6303b106d2656d07cc6bbfac2eec1f91d4c9ce (plain) (tree)
1
2
3
4
5
6
7
8
9


        
                                      
  
                                                


   






                                                                      


                                                          
                                        

   

                    

                            





                                      
                                          



















































                                                        
                                            
 
                                             


   
                                                                   
 
                                         
 
                                                                               

 
                                                                  







                                                           
                                                     




                                  
                                                       

                           

                                                                                     
 


                                                             















                                                         

                                                  









                                    
                                                                        









                                                                   

                                              



                          
                                                                       
































                                                               

                                                 









                                         
                                                                              


























                                                           

                                                 


































                                                           

                                                 

















                                         
                                                                         





                                                                            
                                                  








                              
                                                







                                       
/**
 * @file
 *
 * @ingroup RTEMSBSPsARMLPC24XX_libi2c
 *
 * @brief LibI2C bus driver for the I2C modules.
 */

/*
 * Copyright (c) 2009-2011 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Obere Lagerstr. 30
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#include <bsp.h>
#include <bsp/i2c.h>
#include <bsp/irq.h>
#include <bsp/irq-generic.h>
#include <bsp/system-clocks.h>

#define RTEMS_STATUS_CHECKS_USE_PRINTK

#include <rtems/status-checks.h>

static void lpc24xx_i2c_handler(void *arg)
{
  lpc24xx_i2c_bus_entry *e = arg;
  volatile lpc24xx_i2c *regs = e->regs;
  unsigned state = regs->stat;
  uint8_t *data = e->data;
  uint8_t *end = e->end;
  bool notify = true;

  switch (state) {
    case 0x28U:
      /* Data has been transmitted successfully */
      if (data != end) {
        regs->dat = *data;
        ++data;
        regs->conset = LPC24XX_I2C_AA;
        regs->conclr = LPC24XX_I2C_SI;
        notify = false;
        e->data = data;
      }
      break;
    case 0x50U:
      /* Data has been received */
      if (data != end) {
        *data = (uint8_t) regs->dat;
        ++data;
        if (data != end) {
          if (data + 1 != end) {
            regs->conset = LPC24XX_I2C_AA;
          } else {
            regs->conclr = LPC24XX_I2C_AA;
          }
          regs->conclr = LPC24XX_I2C_SI;
          notify = false;
          e->data = data;
        } else {
          /* This is an error and should never happen */
        }
      }
      break;
    case 0x58U:
      /* Last data has been received */
      if (data != end) {
        *data = (uint8_t) regs->dat;
      }
      break;
    default:
      /* Do nothing */
      break;
  }

  /* Notify task if necessary */
  if (notify) {
    bsp_interrupt_vector_disable(e->vector);

    rtems_semaphore_release(e->state_update);
  }
}

static rtems_status_code lpc24xx_i2c_wait(lpc24xx_i2c_bus_entry *e)
{
  bsp_interrupt_vector_enable(e->vector);

  return rtems_semaphore_obtain(e->state_update, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
}

static rtems_status_code lpc24xx_i2c_init(rtems_libi2c_bus_t *bus)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;
  unsigned cycles = LPC24XX_CCLK / (8U * 100000U * 2U);

  /* Create semaphore */
  sc = rtems_semaphore_create (
    rtems_build_name ('I', '2', 'C', '0' + e->index),
    0,
    RTEMS_SIMPLE_BINARY_SEMAPHORE,
    0,
    &e->state_update
  );
  RTEMS_CHECK_SC(sc, "create status update semaphore");

  /* Enable module power */
  sc = lpc24xx_module_enable(LPC24XX_MODULE_I2C_0 + e->index, LPC24XX_MODULE_CCLK_8);
  RTEMS_CHECK_SC(sc, "enable module");

  /* Pin configuration */
  sc = lpc24xx_pin_config(e->pins, LPC24XX_PIN_SET_FUNCTION);
  RTEMS_CHECK_SC(sc, "pin configuration");

  /* Clock high and low duty cycles */
  regs->sclh = cycles;
  regs->scll = cycles;

  /* Disable module */
  regs->conclr = LPC24XX_I2C_EN;

  /* Install interrupt handler and disable this vector */
  sc = rtems_interrupt_handler_install(
    e->vector,
    "I2C",
    RTEMS_INTERRUPT_UNIQUE,
    lpc24xx_i2c_handler,
    e
  );
  RTEMS_CHECK_SC(sc, "install interrupt handler");
  bsp_interrupt_vector_disable(e->vector);

  /* Enable module in master mode */
  regs->conset = LPC24XX_I2C_EN;

  /* Set self address */
  regs->adr = 0;

  return RTEMS_SUCCESSFUL;
}

static rtems_status_code lpc24xx_i2c_send_start(rtems_libi2c_bus_t *bus)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;

  /* Start */
  regs->conclr = LPC24XX_I2C_STA | LPC24XX_I2C_AA | LPC24XX_I2C_SI;
  regs->conset = LPC24XX_I2C_STA;

  /* Wait */
  sc = lpc24xx_i2c_wait(e);
  RTEMS_CHECK_SC(sc, "wait for state update");

  return RTEMS_SUCCESSFUL;
}

static rtems_status_code lpc24xx_i2c_send_stop(rtems_libi2c_bus_t *bus)
{
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;

  /* Stop */
  regs->conset = LPC24XX_I2C_STO | LPC24XX_I2C_AA;
  regs->conclr = LPC24XX_I2C_STA | LPC24XX_I2C_SI;

  return RTEMS_SUCCESSFUL;
}

static rtems_status_code lpc24xx_i2c_send_addr(
  rtems_libi2c_bus_t *bus,
  uint32_t addr,
  int rw
)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;
  unsigned state = regs->stat;

  /* Check state */
  if (state != 0x8U && state != 0x10U) {
    return -RTEMS_IO_ERROR;
  }

  /* Send address */
  regs->dat = (uint8_t) ((addr << 1U) | ((rw != 0) ? 1U : 0U));
  regs->conset = LPC24XX_I2C_AA;
  regs->conclr = LPC24XX_I2C_STA | LPC24XX_I2C_SI;

  /* Wait */
  sc = lpc24xx_i2c_wait(e);
  RTEMS_CHECK_SC_RV(sc, "wait for state update");

  /* Check state */
  state = regs->stat;
  if (state != 0x18U && state != 0x40U) {
    return -RTEMS_IO_ERROR;
  }

  return RTEMS_SUCCESSFUL;
}

static int lpc24xx_i2c_read(rtems_libi2c_bus_t *bus, unsigned char *in, int n)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;
  unsigned state = regs->stat;
  uint8_t *data = in;
  uint8_t *end = in + n;

  if (n <= 0) {
    return n;
  } else if (state != 0x40U) {
    return -RTEMS_IO_ERROR;
  }

  /* Setup receive buffer */
  e->data = data;
  e->end = end;

  /* Ready to receive data */
  if (data + 1 != end) {
    regs->conset = LPC24XX_I2C_AA;
  } else {
    regs->conclr = LPC24XX_I2C_AA;
  }
  regs->conclr = LPC24XX_I2C_SI;

  /* Wait */
  sc = lpc24xx_i2c_wait(e);
  RTEMS_CHECK_SC_RV(sc, "wait for state update");

  /* Check state */
  state = regs->stat;
  if (state != 0x58U) {
    return -RTEMS_IO_ERROR;
  }

  return n;
}

static int lpc24xx_i2c_write(
  rtems_libi2c_bus_t *bus,
  unsigned char *out,
  int n
)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  lpc24xx_i2c_bus_entry *e = (lpc24xx_i2c_bus_entry *) bus;
  volatile lpc24xx_i2c *regs = e->regs;
  unsigned state = 0;

  if (n <= 0) {
    return n;
  }

  /* Setup transmit buffer */
  e->data = out + 1;
  e->end = out + n;

  /* Transmit first byte */
  regs->dat = *out;
  regs->conset = LPC24XX_I2C_AA;
  regs->conclr = LPC24XX_I2C_SI;

  /* Wait */
  sc = lpc24xx_i2c_wait(e);
  RTEMS_CHECK_SC_RV(sc, "wait for state update");

  /* Check state */
  state = regs->stat;
  if (state != 0x28U) {
    return -RTEMS_IO_ERROR;
  }

  return n;
}

static int lpc24xx_i2c_set_transfer_mode(
  rtems_libi2c_bus_t *bus,
  const rtems_libi2c_tfr_mode_t *mode
)
{
  return -RTEMS_NOT_IMPLEMENTED;
}

static int lpc24xx_i2c_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg)
{
  int rv = -1;
  const rtems_libi2c_tfr_mode_t *tm = (const rtems_libi2c_tfr_mode_t *) arg;

  switch (cmd) {
    case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
      rv = lpc24xx_i2c_set_transfer_mode(bus, tm);
      break;
    default:
      rv = -RTEMS_NOT_DEFINED;
      break;
  }

  return rv;
}

const rtems_libi2c_bus_ops_t lpc24xx_i2c_ops = {
  .init = lpc24xx_i2c_init,
  .send_start = lpc24xx_i2c_send_start,
  .send_stop = lpc24xx_i2c_send_stop,
  .send_addr = lpc24xx_i2c_send_addr,
  .read_bytes = lpc24xx_i2c_read,
  .write_bytes = lpc24xx_i2c_write,
  .ioctl = lpc24xx_i2c_ioctl
};