--- /dev/null
+/*
+ * Copyright © 2021 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+static uint8_t ao_i2c_mutex;
+
+static const uint8_t *i2c_send;
+static uint16_t i2c_send_len;
+static uint8_t *i2c_recv;
+static uint16_t i2c_recv_len;
+static uint8_t i2c_stop;
+static uint8_t i2c_error;
+
+static void
+_ao_i2c_put_byte(void)
+{
+ lpc_i2c.dat = *i2c_send++;
+ lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
+}
+
+static void
+_ao_i2c_get_byte(void)
+{
+ *i2c_recv++ = (uint8_t) lpc_i2c.dat;
+ i2c_recv_len--;
+ if (i2c_recv_len == 0) {
+ lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_AAC);
+ ao_wakeup(&i2c_recv_len);
+ } else {
+ lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
+ }
+}
+
+void
+lpc_i2c_isr(void)
+{
+ switch (lpc_i2c.stat) {
+ case LPC_I2C_STAT_ERROR:
+ lpc_i2c.conset = ((1 << LPC_I2C_CONSET_STO) |
+ (1 << LPC_I2C_CONSET_AA));
+ break;
+ case LPC_I2C_STAT_START:
+ case LPC_I2C_STAT_REPEAT_START:
+ i2c_error = 0;
+ /* fall through ... */
+ case LPC_I2C_STAT_TX_START_ACK:
+ case LPC_I2C_STAT_TX_ACK:
+ --i2c_send_len;
+ if (i2c_send_len) {
+ _ao_i2c_put_byte();
+ } else {
+ if (i2c_stop)
+ lpc_i2c.conset =(1 << LPC_I2C_CONSET_STO);
+ ao_wakeup(&i2c_send_len);
+ }
+ break;
+ case LPC_I2C_STAT_TX_START_NACK:
+ case LPC_I2C_STAT_TX_NACK:
+ lpc_i2c.conset = ((1 << LPC_I2C_CONSET_AA) |
+ (1 << LPC_I2C_CONSET_STO));
+ i2c_send_len = 0;
+ i2c_error = 1;
+ ao_wakeup(&i2c_send_len);
+ break;
+ case LPC_I2C_STAT_TX_ARB_LOST:
+ lpc_i2c.conset =((1 << LPC_I2C_CONSET_AA)|
+ (1 << LPC_I2C_CONSET_STA));
+ break;
+ case LPC_I2C_STAT_RX_START_ACK:
+ lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
+ break;
+ case LPC_I2C_STAT_RX_START_NACK:
+ case LPC_I2C_STAT_RX_NACK:
+ lpc_i2c.conset = ((1 << LPC_I2C_CONSET_AA) |
+ (1 << LPC_I2C_CONSET_STO));
+ i2c_recv_len = 0;
+ i2c_error = 1;
+ ao_wakeup(&i2c_recv_len);
+ break;
+ case LPC_I2C_STAT_RX_ACK:
+ if (i2c_recv_len)
+ _ao_i2c_get_byte();
+ break;
+ }
+ lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
+}
+
+void
+ao_i2c_get(uint8_t index)
+{
+ (void) index;
+ ao_mutex_get(&ao_i2c_mutex);
+ lpc_i2c.conset = (1 << LPC_I2C_CONSET_I2EN);
+}
+
+void
+ao_i2c_put(uint8_t index)
+{
+ (void) index;
+ lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_I2ENC);
+ ao_mutex_put(&ao_i2c_mutex);
+}
+
+uint8_t
+ao_i2c_start(uint8_t index, uint16_t addr)
+{
+ uint8_t a = (uint8_t) addr;
+
+ (void) index;
+ ao_arch_block_interrupts();
+ i2c_send = &a;
+ i2c_send_len = 1;
+ i2c_stop = 0;
+ lpc_i2c.conset = (1 << LPC_I2C_CONSET_STA);
+ while (i2c_send_len)
+ ao_sleep(&i2c_send_len);
+ ao_arch_release_interrupts();
+ return 0;
+}
+
+uint8_t
+ao_i2c_send(const void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+ (void) index;
+ ao_arch_block_interrupts();
+ i2c_send = block;
+ i2c_send_len = len;
+ i2c_stop = stop;
+ _ao_i2c_put_byte();
+ while (i2c_send_len)
+ ao_sleep(&i2c_send_len);
+ ao_arch_release_interrupts();
+ return 0;
+}
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+ (void) index;
+ ao_arch_block_interrupts();
+ i2c_recv = block;
+ i2c_recv_len = len;
+ i2c_stop = stop;
+ /* Check to see if a byte is already here */
+ if (lpc_i2c.stat == LPC_I2C_STAT_RX_ACK)
+ _ao_i2c_get_byte();
+ while (i2c_recv_len)
+ ao_sleep(&i2c_recv_len);
+ ao_arch_release_interrupts();
+ return 0;
+}
+
+void
+ao_i2c_init(void)
+{
+ /* Configure pins */
+ lpc_ioconf.pio0_4 = ao_lpc_alternate(LPC_IOCONF_FUNC_I2C_SCL);
+ lpc_ioconf.pio0_5 = ao_lpc_alternate(LPC_IOCONF_FUNC_I2C_SDA);
+
+ /* Enable the device */
+ lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_I2C);
+
+ /* Reset the device */
+ lpc_scb.presetctrl &= ~(1UL << LPC_SCB_PRESETCTRL_I2C_RST_N);
+ lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_I2C_RST_N);
+}
#define LPC_SCB_PDRUNCFG_USBPLL_PD 8
#define LPC_SCB_PDRUNCFG_USBPAD_PD 10
+struct lpc_i2c {
+ vuint32_t conset;
+ vuint32_t stat;
+ vuint32_t dat;
+ vuint32_t adr0;
+
+ vuint32_t sclh;
+ vuint32_t scll;
+ vuint32_t conclr;
+ vuint32_t mmctrl;
+
+ vuint32_t adr[3];
+ vuint32_t data_buffer;
+
+ vuint32_t mask[4];
+};
+
+extern struct lpc_i2c lpc_i2c;
+
+#define lpc_i2c (*(struct lpc_i2c *) 0x40000000)
+
+#define LPC_I2C_CONSET_AA 2
+#define LPC_I2C_CONSET_SI 3
+#define LPC_I2C_CONSET_STO 4
+#define LPC_I2C_CONSET_STA 5
+#define LPC_I2C_CONSET_I2EN 6
+
+/* master status values */
+#define LPC_I2C_STAT_ERROR 0x00
+#define LPC_I2C_STAT_START 0x08
+#define LPC_I2C_STAT_REPEAT_START 0x10
+#define LPC_I2C_STAT_TX_START_ACK 0x18
+#define LPC_I2C_STAT_TX_START_NACK 0x20
+#define LPC_I2C_STAT_TX_ACK 0x28
+#define LPC_I2C_STAT_TX_NACK 0x30
+#define LPC_I2C_STAT_TX_ARB_LOST 0x38
+#define LPC_I2C_STAT_RX_START_ACK 0x40
+#define LPC_I2C_STAT_RX_START_NACK 0x48
+#define LPC_I2C_STAT_RX_ACK 0x50
+#define LPC_I2C_STAT_RX_NACK 0x58
+
+
+#define LPC_I2C_ADR_GC 0
+#define LPC_I2C_ADR_ADDRESS 1
+
+#define LPC_I2C_CONCLR_AAC 2
+#define LPC_I2C_CONCLR_SIC 3
+#define LPC_I2C_CONCLR_STAC 5
+#define LPC_I2C_CONCLR_I2ENC 6
+
+#define LPC_I2C_MMCTRL_MM_ENA 0
+#define LPC_I2C_MMCTRL_ENA_SCL 1
+#define LPC_I2C_MMCTRL_MATCH_ALL 2
+
+#define LPC_I2C_MASK_MASK 1
+
struct lpc_flash {
uint32_t r0[4]; /* 0x0 */