/** * @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 * * * 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 #include #include #include #include #define RTEMS_STATUS_CHECKS_USE_PRINTK #include 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 };