twi: Correct swapped twi irq names
[simavr] / simavr / sim / avr_twi.c
index 589e18e..782eef4 100644 (file)
 #include <stdio.h>
 #include "avr_twi.h"
 
-static uint8_t avr_twi_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
+/*
+ * This block respectfully nicked straight out from the Atmel sample
+ * code for AVR315. Typos and all.
+ * There is no copyright notice on the original file.
+ */
+/****************************************************************************
+  TWI State codes
+****************************************************************************/
+// General TWI Master status codes
+#define TWI_START                  0x08  // START has been transmitted
+#define TWI_REP_START              0x10  // Repeated START has been transmitted
+#define TWI_ARB_LOST               0x38  // Arbitration lost
+
+// TWI Master Transmitter status codes
+#define TWI_MTX_ADR_ACK            0x18  // SLA+W has been transmitted and ACK received
+#define TWI_MTX_ADR_NACK           0x20  // SLA+W has been transmitted and NACK received
+#define TWI_MTX_DATA_ACK           0x28  // Data byte has been transmitted and ACK received
+#define TWI_MTX_DATA_NACK          0x30  // Data byte has been transmitted and NACK received
+
+// TWI Master Receiver status codes
+#define TWI_MRX_ADR_ACK            0x40  // SLA+R has been transmitted and ACK received
+#define TWI_MRX_ADR_NACK           0x48  // SLA+R has been transmitted and NACK received
+#define TWI_MRX_DATA_ACK           0x50  // Data byte has been received and ACK transmitted
+#define TWI_MRX_DATA_NACK          0x58  // Data byte has been received and NACK transmitted
+
+// TWI Slave Transmitter status codes
+#define TWI_STX_ADR_ACK            0xA8  // Own SLA+R has been received; ACK has been returned
+#define TWI_STX_ADR_ACK_M_ARB_LOST 0xB0  // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
+#define TWI_STX_DATA_ACK           0xB8  // Data byte in TWDR has been transmitted; ACK has been received
+#define TWI_STX_DATA_NACK          0xC0  // Data byte in TWDR has been transmitted; NOT ACK has been received
+#define TWI_STX_DATA_ACK_LAST_BYTE 0xC8  // Last data byte in TWDR has been transmitted (TWEA = �0�); ACK has been received
+
+// TWI Slave Receiver status codes
+#define TWI_SRX_ADR_ACK            0x60  // Own SLA+W has been received ACK has been returned
+#define TWI_SRX_ADR_ACK_M_ARB_LOST 0x68  // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK            0x70  // General call address has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK_M_ARB_LOST 0x78  // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_ACK       0x80  // Previously addressed with own SLA+W; data has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_NACK      0x88  // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
+#define TWI_SRX_GEN_DATA_ACK       0x90  // Previously addressed with general call; data has been received; ACK has been returned
+#define TWI_SRX_GEN_DATA_NACK      0x98  // Previously addressed with general call; data has been received; NOT ACK has been returned
+#define TWI_SRX_STOP_RESTART       0xA0  // A STOP condition or repeated START condition has been received while still addressed as Slave
+
+// TWI Miscellaneous status codes
+#define TWI_NO_STATE               0xF8  // No relevant state information available; TWINT = �0�
+#define TWI_BUS_ERROR              0x00  // Bus error due to an illegal START or STOP condition
+
+#define AVR_TWI_DEBUG 0
+
+static inline void
+_avr_twi_status_set(
+               avr_twi_t * p,
+               uint8_t v,
+               int interrupt)
 {
-//     avr_twi_t * p = (avr_twi_t *)param;
-//     uint8_t v = p->input_data_register;
-//     p->input_data_register = 0;
-//     printf("avr_twi_read = %02x\n", v);
+       avr_regbit_setto_raw(p->io.avr, p->twsr, v);
+#if AVR_TWI_DEBUG
+       printf("_avr_twi_status_set %02x\n", v);
+#endif
+       avr_raise_irq(p->io.irq + TWI_IRQ_STATUS, v);
+       if (interrupt)
+               avr_raise_interrupt(p->io.avr, &p->twi);
+}
+
+static inline uint8_t
+_avr_twi_status_get(
+               avr_twi_t * p)
+{
+       return avr_regbit_get_raw(p->io.avr, p->twsr);
+}
+
+static avr_cycle_count_t
+avr_twi_set_state_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_twi_t * p = (avr_twi_t *)param;
+       _avr_twi_status_set(p, p->next_twstate, 1);
+       p->next_twstate = 0;
        return 0;
 }
 
-static void avr_twi_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+/*
+ * This is supposed to trigger a timer whose duration is a multiple
+ * of 'twi' clock cycles, which should be derived from the prescaler
+ * (100khz, 400khz etc).
+ * Right now it cheats and uses one twi cycle == one usec.
+ */
+static void
+_avr_twi_delay_state(
+               avr_twi_t * p,
+               int twi_cycles,
+               uint8_t state)
+{
+       p->next_twstate = state;
+       // TODO: calculate clock rate, convert to cycles, and use that
+       avr_cycle_timer_register_usec(
+                       p->io.avr, twi_cycles, avr_twi_set_state_timer, p);
+}
+
+static void
+avr_twi_write(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
 {
-#if 0
        avr_twi_t * p = (avr_twi_t *)param;
-       if (addr == p->r_spdr) {
-//             printf("avr_twi_write = %02x\n", v);
-               avr_core_watch_write(avr, addr, v);
-
-               if (avr_regbit_get(avr, p->spe)) {
-                       // in master mode, any byte is sent as it comes..
-                       if (avr_regbit_get(avr, p->mstr)) {
-                               avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, v);
+
+       uint8_t twen = avr_regbit_get(avr, p->twen);
+       uint8_t twsta = avr_regbit_get(avr, p->twsta);
+       uint8_t twsto = avr_regbit_get(avr, p->twsto);
+       uint8_t twint = avr_regbit_get(avr, p->twi.raised);
+
+       avr_core_watch_write(avr, addr, v);
+#if AVR_TWI_DEBUG
+       printf("avr_twi_write %02x START:%d STOP:%d ACK:%d INT:%d TWSR:%02x (state %02x)\n", v,
+                       avr_regbit_get(avr, p->twsta),
+                       avr_regbit_get(avr, p->twsto),
+                       avr_regbit_get(avr, p->twea),
+                       avr_regbit_get(avr, p->twi.raised),
+                       avr_regbit_get_raw(p->io.avr, p->twsr), p->state);
+#endif
+       if (twen != avr_regbit_get(avr, p->twen)) {
+               twen = !twen;
+               if (!twen) { // if we were running, now now are not
+                       avr_regbit_clear(avr, p->twea);
+                       avr_regbit_clear(avr, p->twsta);
+                       avr_regbit_clear(avr, p->twsto);
+                       avr_clear_interrupt(avr, &p->twi);
+                       avr_core_watch_write(avr, p->r_twdr, 0xff);
+                       _avr_twi_status_set(p, TWI_NO_STATE, 0);
+                       p->state = 0;
+                       p->peer_addr = 0;
+               }
+               printf("TWEN: %d\n", twen);
+       }
+       if (!twen)
+               return;
+
+       uint8_t cleared = avr_regbit_get(avr, p->twi.raised);
+
+       /*int cleared = */avr_clear_interrupt_if(avr, &p->twi, twint);
+//     printf("cleared %d\n", cleared);
+
+       if (!twsto && avr_regbit_get(avr, p->twsto)) {
+               // generate a stop condition
+#if AVR_TWI_DEBUG
+               printf("<<<<< I2C stop\n");
+#endif
+               if (p->state) { // doing stuff
+                       if (p->state & TWI_COND_START) {
+                               avr_raise_irq(p->io.irq + TWI_IRQ_MOSI,
+                                               avr_twi_irq_msg(TWI_COND_STOP, p->peer_addr, 1));
                        }
                }
+               p->state = 0;
        }
+       if (!twsta && avr_regbit_get(avr, p->twsta)) {
+#if AVR_TWI_DEBUG
+               printf(">>>>> I2C %sstart\n", p->state & TWI_COND_START ? "RE" : "");
+#endif
+               // generate a start condition
+               if (p->state & TWI_COND_START)
+                       _avr_twi_delay_state(p, 3, TWI_REP_START);
+               else
+                       _avr_twi_delay_state(p, 3, TWI_START);
+               p->peer_addr = 0;
+               p->state = TWI_COND_START;
+       }
+
+       if (cleared &&
+                       !avr_regbit_get(avr, p->twsta) &&
+                       !avr_regbit_get(avr, p->twsto)) {
+               // writing or reading a byte
+               if (p->state & TWI_COND_ADDR) {
+                       int do_read = p->peer_addr & 1;
+#if AVR_TWI_DEBUG
+                       if (do_read)
+                               printf("I2C READ byte from %02x\n", p->peer_addr);
+                       else
+                               printf("I2C WRITE byte %02x to %02x\n", avr->data[p->r_twdr], p->peer_addr);
+#endif
+                       // a normal data byte
+                       uint8_t msgv = do_read ? TWI_COND_READ : TWI_COND_WRITE;
+
+                       if (avr_regbit_get(avr, p->twea))
+                               msgv |= TWI_COND_ACK;
+
+                       p->state &= ~TWI_COND_ACK;      // clear ACK bit
+
+                       // if the latch is ready... as set by writing/reading the TWDR
+                       if ((p->state & msgv)) {
+
+                               // we send an IRQ and we /expect/ a slave to reply
+                               // immediately via an IRQ to set the COND_ACK bit
+                               // otherwise it's assumed it's been nacked...
+                               avr_raise_irq(p->io.irq + TWI_IRQ_MOSI,
+                                               avr_twi_irq_msg(msgv, p->peer_addr, avr->data[p->r_twdr]));
+
+                               if (do_read) { // read ?
+                                       _avr_twi_delay_state(p, 9,
+                                                       msgv & TWI_COND_ACK ?
+                                                                       TWI_MRX_DATA_ACK : TWI_MRX_DATA_NACK);
+                               } else {
+                                       _avr_twi_delay_state(p, 9,
+                                                       p->state & TWI_COND_ACK ?
+                                                                       TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK);
+                               }
+                       }
+#if AVR_TWI_DEBUG
+                       else
+                               printf("I2C latch is not ready, do nothing\n");
 #endif
+               } else {
+#if AVR_TWI_DEBUG
+                       printf("I2C Master address %02x\n", avr->data[p->r_twdr]);
+#endif
+                       // send the address
+                       p->state |= TWI_COND_ADDR;
+                       p->peer_addr = avr->data[p->r_twdr];
+                       p->state &= ~TWI_COND_ACK;      // clear ACK bit
+
+                       // we send an IRQ and we /expect/ a slave to reply
+                       // immediately via an IRQ tp set the COND_ACK bit
+                       // otherwise it's assumed it's been nacked...
+                       avr_raise_irq(p->io.irq + TWI_IRQ_MOSI,
+                                       avr_twi_irq_msg(TWI_COND_START, p->peer_addr, 0));
+
+                       if (p->peer_addr & 1) { // read ?
+                               p->state |= TWI_COND_READ;      // always allow read to start with
+                               _avr_twi_delay_state(p, 9,
+                                               p->state & TWI_COND_ACK ?
+                                                               TWI_MRX_ADR_ACK : TWI_MRX_ADR_NACK);
+                       } else {
+                               _avr_twi_delay_state(p, 9,
+                                               p->state & TWI_COND_ACK ?
+                                                               TWI_MTX_ADR_ACK : TWI_MTX_ADR_NACK);
+                       }
+               }
+               p->state &= ~TWI_COND_WRITE;
+       }
 }
 
-#if 0
-static void avr_twi_irq_input(struct avr_irq_t * irq, uint32_t value, void * param)
+/*
+ * Write data to the latch, tell the system we have something
+ * to send next
+ */
+static void
+avr_twi_write_data(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
 {
        avr_twi_t * p = (avr_twi_t *)param;
-       avr_t * avr = p->io.avr;
 
-       // check to see if we are enabled
-       if (!avr_regbit_get(avr, p->twen))
-               return;
-#if 0
-       // double buffer the input.. ?
-       p->input_data_register = value;
-       avr_raise_interrupt(avr, &p->twi);
-
-       // if in slave mode, 
-       // 'output' the byte only when we received one...
-       if (!avr_regbit_get(avr, p->mstr)) {
-               avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, avr->data[p->r_spdr]);
-       }
-#endif
+       avr_core_watch_write(avr, addr, v);
+       // tell system we have something in the write latch
+       p->state |= TWI_COND_WRITE;
 }
 
-       // handle a data write, after a (re)start
-static int twi_slave_write(struct twi_slave_t* p, uint8_t v)
+/*
+ * Read data from the latch, tell the system can receive a new byte
+ */
+static uint8_t
+avr_twi_read_data(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               void * param)
 {
-       return 0;
+       avr_twi_t * p = (avr_twi_t *)param;
+
+       // tell system we can receive another byte
+       p->state |= TWI_COND_READ;
+       return avr->data[p->r_twdr];
 }
 
-       // handle a data read, after a (re)start
-static uint8_t twi_slave_read(struct twi_slave_t* p)
+/*
+ * prevent code from rewriting out status bits, since we actually use them!
+ */
+static void
+avr_twi_write_status(
+               struct avr_t * avr,
+               avr_io_addr_t addr,
+               uint8_t v,
+               void * param)
 {
-       return 0;
+       avr_twi_t * p = (avr_twi_t *)param;
+       uint8_t sr = avr_regbit_get(avr, p->twsr);
+       uint8_t c = avr_regbit_get(avr, p->twps);
+
+       avr_core_watch_write(avr, addr, v);
+       avr_regbit_setto(avr, p->twsr, sr);     // force restore
+
+       if (c != avr_regbit_get(avr, p->twps)) {
+               // prescaler bits changed...
+       }
 }
-#endif
 
-static int avr_twi_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
+static void
+avr_twi_irq_input(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
 {
-       avr_twi_t * p = (avr_twi_t *)port;
-       int res = -1;
+       avr_twi_t * p = (avr_twi_t *)param;
+       avr_t * avr = p->io.avr;
 
-       if (ctl == AVR_IOCTL_TWI_GETSLAVE(p->name)) {
-               *(twi_slave_t**)io_param = &p->slave;
-       } else if (ctl == AVR_IOCTL_TWI_GETBUS(p->name)) {
-               *(twi_bus_t**)io_param = &p->bus;
-       }
+       // check to see if we are enabled
+       if (!avr_regbit_get(avr, p->twen))
+               return;
+       avr_twi_msg_irq_t msg;
+       msg.u.v = value;
 
-       return res;
+       // receiving an acknowledge bit
+       if (msg.u.twi.msg & TWI_COND_ACK) {
+#if AVR_TWI_DEBUG
+               printf("I2C received ACK:%d\n", msg.u.twi.data & 1);
+#endif
+               if (msg.u.twi.data & 1)
+                       p->state |= TWI_COND_ACK;
+               else
+                       p->state &= ~TWI_COND_ACK;
+       }
+       // receive a data byte from a slave
+       if (msg.u.twi.msg & TWI_COND_READ) {
+#if AVR_TWI_DEBUG
+               printf("I2C received %02x\n", msg.u.twi.data);
+#endif
+               avr->data[p->r_twdr] = msg.u.twi.data;
+       }
 }
 
 void avr_twi_reset(struct avr_io_t *io)
 {
-//     avr_twi_t * p = (avr_twi_t *)io;
-       //avr_irq_register_notify(p->io.irq + TWI_IRQ_INPUT, avr_twi_irq_input, p);
+       avr_twi_t * p = (avr_twi_t *)io;
+       avr_irq_register_notify(p->io.irq + TWI_IRQ_MISO, avr_twi_irq_input, p);
 }
 
+static const char * irq_names[TWI_IRQ_COUNT] = {
+       [TWI_IRQ_MISO] = "8<miso",
+       [TWI_IRQ_MOSI] = "32>mosi",
+       [TWI_IRQ_STATUS] = "8>status",
+};
+
 static avr_io_t        _io = {
        .kind = "twi",
        .reset = avr_twi_reset,
-       .ioctl = avr_twi_ioctl,
+       .irq_names = irq_names,
 };
 
 void avr_twi_init(avr_t * avr, avr_twi_t * p)
@@ -115,16 +380,28 @@ void avr_twi_init(avr_t * avr, avr_twi_t * p)
        p->io = _io;
        avr_register_io(avr, &p->io);
        avr_register_vector(avr, &p->twi);
-//     p->slave = slave_driver;        // get default callbacks
-       twi_slave_init(&p->slave, 0, p);
-       twi_bus_init(&p->bus);
 
        //printf("%s TWI%c init\n", __FUNCTION__, p->name);
 
        // allocate this module's IRQ
        avr_io_setirqs(&p->io, AVR_IOCTL_TWI_GETIRQ(p->name), TWI_IRQ_COUNT, NULL);
 
-       avr_register_io_write(avr, p->r_twdr, avr_twi_write, p);
-       avr_register_io_read(avr, p->r_twdr, avr_twi_read, p);
+       avr_register_io_write(avr, p->twen.reg, avr_twi_write, p);
+       avr_register_io_write(avr, p->r_twdr, avr_twi_write_data, p);
+       avr_register_io_read(avr, p->r_twdr, avr_twi_read_data, p);
+       avr_register_io_write(avr, p->twsr.reg, avr_twi_write_status, p);
 }
 
+uint32_t
+avr_twi_irq_msg(
+               uint8_t msg,
+               uint8_t addr,
+               uint8_t data)
+{
+       avr_twi_msg_irq_t _msg = {
+                       .u.twi.msg = msg,
+                       .u.twi.addr = addr,
+                       .u.twi.data = data,
+       };
+       return _msg.u.v;
+}