forked from Imagelibrary/rtems
dev/i2c: Add Xilinx AXI I2C driver.
This is a generic driver for use with Xilinx AXI I2C controller IP. Closes #3100.
This commit is contained in:
@@ -10,6 +10,7 @@ include_dev_i2c_HEADERS += include/dev/i2c/eeprom.h
|
||||
include_dev_i2c_HEADERS += include/dev/i2c/gpio-nxp-pca9535.h
|
||||
include_dev_i2c_HEADERS += include/dev/i2c/i2c.h
|
||||
include_dev_i2c_HEADERS += include/dev/i2c/switch-nxp-pca9548a.h
|
||||
include_dev_i2c_HEADERS += include/dev/i2c/xilinx-axi-i2c.h
|
||||
|
||||
include_dev_spidir = $(includedir)/dev/spi
|
||||
include_dev_spi_HEADERS =
|
||||
@@ -36,6 +37,7 @@ libdev_a_SOURCES += i2c/gpio-nxp-pca9535.c
|
||||
libdev_a_SOURCES += i2c/i2c-bus.c
|
||||
libdev_a_SOURCES += i2c/i2c-dev.c
|
||||
libdev_a_SOURCES += i2c/switch-nxp-pca9548a.c
|
||||
libdev_a_SOURCES += i2c/xilinx-axi-i2c.c
|
||||
libdev_a_SOURCES += spi/spi-bus.c
|
||||
libdev_a_SOURCES += serial/sc16is752.c
|
||||
libdev_a_SOURCES += serial/sc16is752-spi.c
|
||||
|
||||
943
cpukit/dev/i2c/xilinx-axi-i2c.c
Normal file
943
cpukit/dev/i2c/xilinx-axi-i2c.c
Normal file
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org> All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xilinx AXI IIC Interface v2.0. See PG090.pdf.
|
||||
*
|
||||
* Note, only master support is provided and no dynamic mode by design.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <rtems/irq-extension.h>
|
||||
#include <rtems/score/assert.h>
|
||||
|
||||
#include <dev/i2c/i2c.h>
|
||||
#include <dev/i2c/xilinx-axi-i2c.h>
|
||||
|
||||
/*
|
||||
* Register map.
|
||||
*/
|
||||
#define REG_GIE 0x01c
|
||||
#define REG_ISR 0x020
|
||||
#define REG_IER 0x028
|
||||
#define REG_SOFTR 0x040
|
||||
#define REG_CR 0x100
|
||||
#define REG_SR 0x104
|
||||
#define REG_TX_FIFO 0x108
|
||||
#define REG_RX_FIFO 0x10c
|
||||
#define REG_ADR 0x110
|
||||
#define REG_TX_FIFO_OCY 0x114
|
||||
#define REG_RX_FIFO_OCY 0x118
|
||||
#define REG_TEN_ADR 0x11c
|
||||
#define REG_RX_FIFO_PIRQ 0x120
|
||||
#define REG_GPO 0x124
|
||||
#define REG_TSUSTA 0x128
|
||||
#define REG_TSUSTO 0x12c
|
||||
#define REG_THDSTA 0x130
|
||||
#define REG_TSUDAT 0x134
|
||||
#define REG_TBUF 0x138
|
||||
#define REG_THIGH 0x13c
|
||||
#define REG_TLOW 0x140
|
||||
#define REG_THDDAT 0x144
|
||||
|
||||
/*
|
||||
* Interrupts.
|
||||
*/
|
||||
#define INT_ARB_LOST (1 << 0)
|
||||
#define INT_TX_ERROR (1 << 1)
|
||||
#define INT_TX_FIFO_EMPTY (1 << 2)
|
||||
#define INT_RX_FIFO_FULL (1 << 3)
|
||||
#define INT_BUS_NOT_BUSY (1 << 4)
|
||||
#define INT_ADDRESSED_AS_SLAVE (1 << 5)
|
||||
#define INT_NOT_ADDRESSED_AS_SLAVE (1 << 6)
|
||||
#define INT_TX_FIFO_HALF_FULL (1 << 7)
|
||||
#define INT_ALL (0xff)
|
||||
|
||||
/*
|
||||
* Command register.
|
||||
*/
|
||||
#define CR_EN (1 << 0)
|
||||
#define CR_TX_FIFO_RESET (1 << 1)
|
||||
#define CR_MSMS (1 << 2)
|
||||
#define CR_TX (1 << 3)
|
||||
#define CR_TXAK (1 << 4)
|
||||
#define CR_RSTA (1 << 5)
|
||||
#define CR_GC_EN (1 << 6)
|
||||
|
||||
/*
|
||||
* Status register.
|
||||
*/
|
||||
#define SR_ABGC (1 << 0)
|
||||
#define SR_AAS (1 << 1)
|
||||
#define SR_BB (1 << 2)
|
||||
#define SR_SRW (1 << 3)
|
||||
#define SR_TX_FIFO_FULL (1 << 4)
|
||||
#define SR_RX_FIFO_FULL (1 << 5)
|
||||
#define SR_RX_FIFO_EMPTY (1 << 6)
|
||||
#define SR_TX_FIFO_EMPTY (1 << 7)
|
||||
|
||||
/*
|
||||
* FIFO Sizes.
|
||||
*/
|
||||
#define TX_FIFO_SIZE 16
|
||||
#define TX_FIFO_HALF_SIZE (TX_FIFO_SIZE / 2)
|
||||
#define RX_FIFO_SIZE 16
|
||||
|
||||
/*
|
||||
* Address flags.
|
||||
*/
|
||||
#define ADDR_TEN (1 << 31)
|
||||
#define ADDR_GPO (1 << 30)
|
||||
|
||||
typedef struct {
|
||||
i2c_bus base;
|
||||
uint32_t regs;
|
||||
i2c_msg* msgs;
|
||||
uint32_t msgs_remaining;
|
||||
i2c_msg* current_msg;
|
||||
uint32_t current_msg_todo;
|
||||
uint8_t* current_msg_byte;
|
||||
uint32_t current_todo;
|
||||
uint32_t irqstatus;
|
||||
bool read;
|
||||
uint32_t addr;
|
||||
rtems_id task_id;
|
||||
bool gpo_address;
|
||||
xilinx_aix_i2c_timing timing;
|
||||
rtems_vector_number irq;
|
||||
} xilinx_axi_i2c_bus;
|
||||
|
||||
xilinx_axi_i2c_bus* axi_i2c_bus;
|
||||
|
||||
/*
|
||||
* Real-time trace buffering with a small overhead. The data can be dumped from
|
||||
* gdb with:
|
||||
*
|
||||
* define axi-i2c
|
||||
* set $i = 0
|
||||
* while $i < axi_trace_in
|
||||
* printf "%4d %08x %08x %08x : ", \
|
||||
* $i, axi_trace[$i].vars[0], axi_trace[$i].vars[1], axi_trace[$i].vars[2]
|
||||
* output axi_trace[$i].state
|
||||
* printf "\n"
|
||||
* set $i = $i + 1
|
||||
* end
|
||||
* end
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AXI_I2C_NOP,
|
||||
AXI_I2C_BEGIN,
|
||||
AXI_I2C_END,
|
||||
AXI_I2C_TRANSFER,
|
||||
AXI_I2C_ADDRESS,
|
||||
AXI_I2C_START_TRANSFER,
|
||||
AXI_I2C_WRITE,
|
||||
AXI_I2C_READ,
|
||||
AXI_I2C_TX_FIFO,
|
||||
AXI_I2C_RX_FIFO,
|
||||
AXI_I2C_RX_FIFO_LEVEL,
|
||||
AXI_I2C_INT,
|
||||
AXI_I2C_INT_DONE,
|
||||
AXI_I2C_INT_ERROR,
|
||||
AXI_I2C_BUS_NOT_BUSY,
|
||||
AXI_I2C_REG_WRITE,
|
||||
AXI_I2C_REG_READ,
|
||||
AXI_I2C_TIMEOUT,
|
||||
AXI_I2C_WAKE
|
||||
} axi_i2c_state;
|
||||
|
||||
#define RTEMS_AXI_I2C_TRACE 0
|
||||
#if RTEMS_AXI_I2C_TRACE
|
||||
|
||||
#define DRIVER_REG_TRACE 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
axi_i2c_state state;
|
||||
uint32_t vars[3];
|
||||
} axi_i2c_trace;
|
||||
|
||||
#define AXI_I2C_TRACE 5000
|
||||
axi_i2c_trace axi_trace[AXI_I2C_TRACE];
|
||||
int axi_trace_in;
|
||||
|
||||
static inline void axi_trace_reset(void)
|
||||
{
|
||||
axi_trace_in = 0;
|
||||
}
|
||||
|
||||
static inline void axi_trace_append(axi_i2c_state state,
|
||||
uint32_t v1,
|
||||
uint32_t v2,
|
||||
uint32_t v3)
|
||||
{
|
||||
if (axi_trace_in < AXI_I2C_TRACE) {
|
||||
axi_i2c_trace rec = { state, { v1, v2, v3 } };
|
||||
axi_trace[axi_trace_in++] = rec;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define axi_trace_reset()
|
||||
#define axi_trace_append(s, v1, v2, v3)
|
||||
#endif
|
||||
|
||||
#define DRIVER_DEBUG 0
|
||||
#define DRIVER_DEBUG_DEFAULT true
|
||||
#if DRIVER_DEBUG
|
||||
#ifndef RTEMS_PRINTFLIKE
|
||||
#define RTEMS_PRINTFLIKE( _format_pos, _ap_pos ) \
|
||||
__attribute__((__format__(__printf__, _format_pos, _ap_pos)))
|
||||
#endif
|
||||
static bool drv_printk_enable = DRIVER_DEBUG_DEFAULT;
|
||||
static void drv_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
||||
static void
|
||||
drv_printk(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (drv_printk_enable)
|
||||
vprintk(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#else
|
||||
#define drv_printk(_fmt, ...)
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_reg_write(const xilinx_axi_i2c_bus* bus, uint32_t reg, uint32_t value)
|
||||
{
|
||||
#if DRIVER_REG_TRACE
|
||||
axi_trace_append(AXI_I2C_REG_WRITE, reg, value, 0);
|
||||
#endif
|
||||
*((volatile uint32_t*) (bus->regs + reg))= value;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_reg_read(const xilinx_axi_i2c_bus* bus, uint32_t reg)
|
||||
{
|
||||
uint32_t value = *((volatile uint32_t*) (bus->regs + reg));
|
||||
#if DRIVER_REG_TRACE
|
||||
axi_trace_append(AXI_I2C_REG_READ, reg, value, 0);
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
xilinx_axi_i2c_read_irq_status(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
return xilinx_axi_i2c_reg_read(bus, REG_ISR);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
xilinx_axi_i2c_read_irq_enabled(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
return xilinx_axi_i2c_reg_read(bus, REG_IER);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_clear_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
/*
|
||||
* The ISR bits can be toggled so only write a 1 if set.
|
||||
*/
|
||||
xilinx_axi_i2c_reg_write(bus, REG_ISR,
|
||||
xilinx_axi_i2c_reg_read(bus, REG_ISR) & mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_enable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_IER,
|
||||
xilinx_axi_i2c_reg_read(bus, REG_IER) | mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_disable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_IER,
|
||||
xilinx_axi_i2c_reg_read(bus, REG_IER) & ~mask);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_clear_enable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_clear_irq(bus, mask);
|
||||
xilinx_axi_i2c_enable_irq(bus, mask);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_disable_clear_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_disable_irq(bus, mask);
|
||||
xilinx_axi_i2c_clear_irq(bus, mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_disable_all_irq(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_GIE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_enable_interrupts(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_GIE, 1 << 31);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_disable_interrupts(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_GIE, 0);
|
||||
xilinx_axi_i2c_reg_write(bus, REG_IER, 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_write_cr(const xilinx_axi_i2c_bus* bus, uint32_t value)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_CR, value);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_read_cr(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
return xilinx_axi_i2c_reg_read(bus, REG_CR);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_set_cr(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_CR,
|
||||
xilinx_axi_i2c_reg_read(bus, REG_CR) | mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_clear_cr(const xilinx_axi_i2c_bus* bus, uint32_t mask)
|
||||
{
|
||||
xilinx_axi_i2c_write_cr(bus, xilinx_axi_i2c_read_cr(bus) & ~mask);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_read_sr(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
return xilinx_axi_i2c_reg_read(bus, REG_SR);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_read_rx_level(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
if ((xilinx_axi_i2c_read_sr(bus) & SR_RX_FIFO_EMPTY) != 0)
|
||||
return 0;
|
||||
return xilinx_axi_i2c_reg_read(bus, REG_RX_FIFO_OCY) + 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
xilinx_axi_i2c_write_rx_pirq(const xilinx_axi_i2c_bus* bus, uint32_t level)
|
||||
{
|
||||
if (level != 0)
|
||||
xilinx_axi_i2c_reg_write(bus, REG_RX_FIFO_PIRQ, level - 1);
|
||||
else
|
||||
xilinx_axi_i2c_reg_write(bus, REG_RX_FIFO_PIRQ, level);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_read_tx_space(const xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
if ((xilinx_axi_i2c_read_sr(bus) & SR_TX_FIFO_EMPTY) != 0)
|
||||
return TX_FIFO_SIZE;
|
||||
return TX_FIFO_SIZE - xilinx_axi_i2c_reg_read(bus, REG_TX_FIFO_OCY) - 1;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_write_tx_fifo_data(xilinx_axi_i2c_bus* bus, uint32_t data)
|
||||
{
|
||||
axi_trace_append(AXI_I2C_TX_FIFO, data, 0, 0);
|
||||
xilinx_axi_i2c_reg_write(bus, REG_TX_FIFO, data);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xilinx_axi_i2c_read_rx_fifo_data(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
uint32_t data = xilinx_axi_i2c_reg_read(bus, REG_RX_FIFO);
|
||||
axi_trace_append(AXI_I2C_RX_FIFO, data, 0, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_reset(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
xilinx_axi_i2c_reg_write(bus, REG_SOFTR, 0x0a);
|
||||
if ((bus->timing.valid_mask & XILINX_AIX_I2C_ALL_REGS) != 0)
|
||||
{
|
||||
static const uint32_t r[8] = {
|
||||
REG_TSUSTA,
|
||||
REG_TSUSTO,
|
||||
REG_THDSTA,
|
||||
REG_TSUDAT,
|
||||
REG_TBUF,
|
||||
REG_THIGH,
|
||||
REG_TLOW,
|
||||
REG_THDDAT
|
||||
};
|
||||
static const uint32_t m[8] = {
|
||||
XILINX_AIX_I2C_TSUSTA,
|
||||
XILINX_AIX_I2C_TSUSTO,
|
||||
XILINX_AIX_I2C_THDSTA,
|
||||
XILINX_AIX_I2C_TSUDAT,
|
||||
XILINX_AIX_I2C_TBUF,
|
||||
XILINX_AIX_I2C_THIGH,
|
||||
XILINX_AIX_I2C_TLOW,
|
||||
XILINX_AIX_I2C_THDDAT
|
||||
};
|
||||
uint32_t vm = bus->timing.valid_mask;
|
||||
const uint32_t* u = &bus->timing.TSUSTA;
|
||||
size_t i;
|
||||
for (i = 0; i < (sizeof(r) / sizeof(r[0])); ++i, ++u) {
|
||||
if ((vm & m[i]) != 0) {
|
||||
xilinx_axi_i2c_reg_write(bus, r[i], *u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_reinit(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
drv_printk("axi-i2c: reinit\n");
|
||||
xilinx_axi_i2c_reset(bus);
|
||||
xilinx_axi_i2c_write_rx_pirq(bus, RX_FIFO_SIZE);
|
||||
xilinx_axi_i2c_write_cr(bus, CR_TX_FIFO_RESET);
|
||||
xilinx_axi_i2c_write_cr(bus, CR_EN);
|
||||
xilinx_axi_i2c_clear_enable_irq(bus, INT_ARB_LOST);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_wakeup(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
axi_trace_append(AXI_I2C_WAKE, bus->task_id, bus->irqstatus, 0);
|
||||
drv_printk("axi-i2c: wakeup: irqstatus: %08lx\n", bus->irqstatus);
|
||||
rtems_status_code sc = rtems_event_transient_send(bus->task_id);
|
||||
_Assert(sc == RTEMS_SUCCESSFUL);
|
||||
(void) sc;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_next_byte(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
--bus->current_todo;
|
||||
--bus->current_msg_todo;
|
||||
++bus->current_msg_byte;
|
||||
if (bus->current_msg_todo == 0) {
|
||||
if (bus->msgs_remaining != 0 &&
|
||||
(bus->msgs[0].flags & I2C_M_NOSTART) != 0) {
|
||||
bus->current_msg_todo = bus->msgs[0].len;
|
||||
bus->current_msg_byte = bus->msgs[0].buf;
|
||||
++bus->msgs;
|
||||
--bus->msgs_remaining;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_read_rx_byte(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
*bus->current_msg_byte = (uint8_t) xilinx_axi_i2c_read_rx_fifo_data(bus);
|
||||
xilinx_axi_i2c_next_byte(bus);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_read_rx_bytes(xilinx_axi_i2c_bus* bus, uint32_t count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
xilinx_axi_i2c_read_rx_byte(bus);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_set_rx_fifo_level(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
uint32_t size;
|
||||
if (bus->current_todo > RX_FIFO_SIZE) {
|
||||
size = RX_FIFO_SIZE;
|
||||
} else {
|
||||
size = bus->current_todo - 1;
|
||||
}
|
||||
axi_trace_append(AXI_I2C_RX_FIFO_LEVEL, size, 0, 0);
|
||||
xilinx_axi_i2c_write_rx_pirq(bus, size);
|
||||
}
|
||||
|
||||
static bool xilinx_axi_i2c_start_transfer(xilinx_axi_i2c_bus* bus);
|
||||
|
||||
static bool
|
||||
xilinx_axi_i2c_read_rx_fifo(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
drv_printk("axi-i2c: read rx fifo: length:%lu\n", bus->current_todo);
|
||||
|
||||
if (bus->current_todo == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((xilinx_axi_i2c_read_sr(bus) & SR_RX_FIFO_EMPTY) == 0) {
|
||||
uint32_t level = xilinx_axi_i2c_read_rx_level(bus);
|
||||
bool active;
|
||||
|
||||
drv_printk("axi-i2c: read rx fifo: level:%lu\n", level);
|
||||
|
||||
if (level > bus->current_todo)
|
||||
level = bus->current_todo;
|
||||
|
||||
switch (bus->current_todo - level) {
|
||||
case 1:
|
||||
drv_printk("axi-i2c: read rx fifo: one more\n");
|
||||
/*
|
||||
* One more byte to be received. This is set up by programming the RX
|
||||
* FIFO programmable depth interrupt register with a value that is 2
|
||||
* less than the number we need (the register is minus 1). When we have
|
||||
* one byte left disable the TX error interrupt because setting the NO
|
||||
* ACK bit in the command register causes a TX error interrupt. Set the
|
||||
* TXAK bit in the CR to not-acknowledge the next byte received telling
|
||||
* the slave sender the master accepts no more data, then read the
|
||||
* FIFO. If the FIFO is ready before the TXAK bit is set the slave will
|
||||
* see a request for more data. We will come back to the next case
|
||||
* statement for the last byte once it has been received.
|
||||
*/
|
||||
xilinx_axi_i2c_disable_clear_irq(bus, INT_TX_ERROR);
|
||||
xilinx_axi_i2c_set_cr(bus, CR_TXAK);
|
||||
xilinx_axi_i2c_write_rx_pirq(bus, 0);
|
||||
xilinx_axi_i2c_read_rx_bytes(bus, level);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
drv_printk("axi-i2c: read rx fifo: no more\n");
|
||||
/*
|
||||
* We should have 1 byte in the FIFO which is the last byte received
|
||||
* with a NACK. If there are no more message we need to send a STOP by
|
||||
* clearing he MSMS bit in the CR and then waiting for the bus to not
|
||||
* be busy.
|
||||
*/
|
||||
xilinx_axi_i2c_disable_clear_irq(bus,
|
||||
INT_RX_FIFO_FULL | INT_TX_ERROR);
|
||||
if (bus->msgs_remaining == 0) {
|
||||
xilinx_axi_i2c_clear_cr(bus, CR_MSMS);
|
||||
xilinx_axi_i2c_clear_enable_irq(bus, INT_BUS_NOT_BUSY);
|
||||
active = true;
|
||||
}
|
||||
xilinx_axi_i2c_read_rx_byte(bus);
|
||||
if (bus->msgs_remaining != 0)
|
||||
active = xilinx_axi_i2c_start_transfer(bus);
|
||||
return active;
|
||||
|
||||
default:
|
||||
drv_printk("axi-i2c: read rx fifo: more:%lu\n", bus->current_todo - level);
|
||||
/*
|
||||
* All the requested data is in the FIFO so read it and update the PIRQ
|
||||
* level. The PIRQ size is always one less than the maximum size.
|
||||
*/
|
||||
xilinx_axi_i2c_read_rx_bytes(bus, level);
|
||||
if (bus->current_todo > RX_FIFO_SIZE) {
|
||||
xilinx_axi_i2c_write_rx_pirq(bus, RX_FIFO_SIZE);
|
||||
} else {
|
||||
xilinx_axi_i2c_write_rx_pirq(bus, bus->current_todo - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_write_tx_byte(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
xilinx_axi_i2c_write_tx_fifo_data(bus, *bus->current_msg_byte);
|
||||
xilinx_axi_i2c_next_byte(bus);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_write_tx_bytes(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
uint32_t space = xilinx_axi_i2c_read_tx_space(bus);
|
||||
uint32_t level = bus->current_todo - 1;
|
||||
uint32_t i;
|
||||
drv_printk("axi-i2c: tx fifo load: space:%lu level:%lu\n", space, level);
|
||||
if (level < space)
|
||||
space = level;
|
||||
for (i = 0; i < space; ++i)
|
||||
xilinx_axi_i2c_write_tx_byte(bus);
|
||||
}
|
||||
|
||||
static bool
|
||||
xilinx_axi_i2c_write_tx_fifo(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
bool more = true;
|
||||
drv_printk("axi-i2c: write tx fifo: current_todo: %lu\n", bus->current_todo);
|
||||
switch (bus->current_todo) {
|
||||
case 0:
|
||||
xilinx_axi_i2c_disable_clear_irq(bus,
|
||||
INT_TX_FIFO_EMPTY |
|
||||
INT_TX_FIFO_HALF_FULL |
|
||||
INT_TX_ERROR |
|
||||
INT_BUS_NOT_BUSY);
|
||||
more = xilinx_axi_i2c_start_transfer(bus);
|
||||
break;
|
||||
case 1:
|
||||
/*
|
||||
* If transmitting and the last byte issue a stop and wait for the bus to
|
||||
* not be busy.
|
||||
*/
|
||||
if (!bus->read && bus->msgs_remaining == 0) {
|
||||
xilinx_axi_i2c_clear_cr(bus, CR_MSMS);
|
||||
xilinx_axi_i2c_clear_enable_irq(bus, INT_BUS_NOT_BUSY);
|
||||
}
|
||||
xilinx_axi_i2c_write_tx_byte(bus);
|
||||
break;
|
||||
default:
|
||||
xilinx_axi_i2c_write_tx_bytes(bus);
|
||||
break;
|
||||
}
|
||||
return more;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_write_address(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
if ((bus->addr & ADDR_GPO) != 0)
|
||||
xilinx_axi_i2c_reg_write(bus, REG_GPO, (bus->addr >> 12) & 0xf);
|
||||
if ((bus->addr & ADDR_TEN) != 0)
|
||||
xilinx_axi_i2c_write_tx_fifo_data(bus, (bus->addr >> 8) & 0xff);
|
||||
xilinx_axi_i2c_write_tx_fifo_data(bus, bus->addr & 0xff);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_start_read(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
uint32_t cr;
|
||||
uint32_t set = INT_RX_FIFO_FULL;
|
||||
axi_trace_append(AXI_I2C_READ, bus->current_todo, 0, 0);
|
||||
drv_printk("axi-i2c: start read: size: %lu\n", bus->current_todo);
|
||||
/*
|
||||
* Is this a restart? If there is no active STOP it is a restart.
|
||||
*/
|
||||
cr = xilinx_axi_i2c_read_cr(bus);
|
||||
if ((cr & CR_MSMS) != 0) {
|
||||
cr |= CR_RSTA;
|
||||
xilinx_axi_i2c_write_cr(bus, cr);
|
||||
}
|
||||
xilinx_axi_i2c_write_address(bus);
|
||||
xilinx_axi_i2c_set_rx_fifo_level(bus);
|
||||
/*
|
||||
* We must NACK the last byte so if we are receiving a single byte issue a
|
||||
* NACK.
|
||||
*/
|
||||
cr &= ~(CR_TX | CR_TXAK);
|
||||
if (bus->current_todo == 1) {
|
||||
cr |= CR_TXAK;
|
||||
} else {
|
||||
set |= INT_TX_ERROR;
|
||||
}
|
||||
/*
|
||||
* Issue a start.
|
||||
*/
|
||||
cr |= CR_MSMS;
|
||||
xilinx_axi_i2c_clear_enable_irq(bus, set);
|
||||
xilinx_axi_i2c_write_cr(bus, cr);
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_start_write(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
uint32_t space;
|
||||
uint32_t enable;
|
||||
uint32_t cr;
|
||||
axi_trace_append(AXI_I2C_WRITE, bus->current_todo, 0, 0);
|
||||
cr = xilinx_axi_i2c_read_cr(bus);
|
||||
/*
|
||||
* If a master issue a restart if there is no active STOP on the bus.
|
||||
*/
|
||||
if ((cr & CR_MSMS) != 0) {
|
||||
cr |= CR_RSTA;
|
||||
xilinx_axi_i2c_write_cr(bus, cr);
|
||||
}
|
||||
xilinx_axi_i2c_write_address(bus);
|
||||
if (bus->current_todo > 1)
|
||||
xilinx_axi_i2c_write_tx_bytes(bus);
|
||||
space = xilinx_axi_i2c_read_tx_space(bus);
|
||||
enable = INT_TX_FIFO_EMPTY | INT_TX_ERROR;
|
||||
if (space > TX_FIFO_HALF_SIZE && bus->current_todo > 1) {
|
||||
enable |= INT_TX_FIFO_HALF_FULL;
|
||||
}
|
||||
xilinx_axi_i2c_clear_enable_irq(bus, enable);
|
||||
cr &= ~CR_TXAK;
|
||||
cr |= CR_MSMS | CR_TX;
|
||||
xilinx_axi_i2c_write_cr(bus, cr);
|
||||
}
|
||||
|
||||
static bool
|
||||
xilinx_axi_i2c_start_transfer(xilinx_axi_i2c_bus* bus)
|
||||
{
|
||||
const i2c_msg* msgs = bus->msgs;
|
||||
uint32_t msg_todo = bus->msgs_remaining;
|
||||
uint32_t i;
|
||||
|
||||
axi_trace_append(AXI_I2C_START_TRANSFER, msg_todo, 0, 0);
|
||||
drv_printk("axi-i2c: start transfer: messages: %lu\n", msg_todo);
|
||||
|
||||
if (msg_todo == 0) {
|
||||
xilinx_axi_i2c_clear_cr(bus, CR_MSMS);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the amount of data to transfer. It can span message buffers if the
|
||||
* I2C_M_NOSTART flag is set.
|
||||
*/
|
||||
bus->current_todo = msgs[0].len;
|
||||
for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) {
|
||||
bus->current_todo += msgs[i].len;
|
||||
}
|
||||
|
||||
bus->read = (msgs->flags & I2C_M_RD) != 0;
|
||||
|
||||
if ((msgs->flags & I2C_M_TEN) != 0) {
|
||||
bus->addr = (ADDR_TEN |
|
||||
((msgs->addr & (3 << 8)) << 1) |
|
||||
((bus->read ? 1 : 0) << 8) |
|
||||
(msgs->addr & 0xff));
|
||||
}
|
||||
else {
|
||||
bus->addr = (msgs->addr & 0x7f) << 1 | (bus->read ? 1 : 0);
|
||||
}
|
||||
|
||||
if (bus->gpo_address)
|
||||
bus->addr |= ADDR_GPO | (msgs->addr & 0xf000);
|
||||
|
||||
axi_trace_append(AXI_I2C_TRANSFER,
|
||||
bus->msgs_remaining,
|
||||
bus->current_todo, bus->addr);
|
||||
|
||||
/*
|
||||
* The bus->msgs is left pointing to the next message because we may need to
|
||||
* start a new message while completing the current message.
|
||||
*/
|
||||
bus->current_msg_todo = msgs[0].len;
|
||||
bus->current_msg_byte = msgs[0].buf;
|
||||
++bus->msgs;
|
||||
--bus->msgs_remaining;
|
||||
|
||||
if (bus->read) {
|
||||
xilinx_axi_i2c_start_read(bus);
|
||||
} else {
|
||||
xilinx_axi_i2c_start_write(bus);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xilinx_axi_i2c_interrupt(void* arg)
|
||||
{
|
||||
xilinx_axi_i2c_bus* bus = arg;
|
||||
uint32_t status = xilinx_axi_i2c_read_irq_status(bus);
|
||||
uint32_t enabled = xilinx_axi_i2c_read_irq_enabled(bus);
|
||||
uint32_t active = status & enabled;
|
||||
uint32_t clear = 0;
|
||||
int done = 0;
|
||||
|
||||
axi_trace_append(AXI_I2C_INT, active, status, enabled);
|
||||
|
||||
drv_printk("axi-i2c: interrupt: active:%02lx isr:%02lx ier:%02lx\n",
|
||||
active, status, enabled);
|
||||
|
||||
/*
|
||||
* An error or we lost arbitration. If transmitting and there is more data to
|
||||
* send a INT_TX_ERROR means the slave issue a NOT ACK because there was not
|
||||
* slave at the address or the addressed slave will not accept any more data.
|
||||
*
|
||||
* Clean up and wake the user.
|
||||
*/
|
||||
if (((active & INT_ARB_LOST) != 0) ||
|
||||
(!bus->read && (active & INT_TX_ERROR) != 0)) {
|
||||
bus->irqstatus = active & (INT_ARB_LOST | INT_TX_ERROR);
|
||||
axi_trace_append(AXI_I2C_INT_ERROR, bus->irqstatus, 0, 0);
|
||||
xilinx_axi_i2c_reinit(bus);
|
||||
xilinx_axi_i2c_clear_cr(bus, CR_EN);
|
||||
xilinx_axi_i2c_wakeup(bus);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* RX FIFO full?
|
||||
*/
|
||||
if ((active & INT_RX_FIFO_FULL) != 0) {
|
||||
clear |= INT_RX_FIFO_FULL;
|
||||
|
||||
if (bus->read && !xilinx_axi_i2c_read_rx_fifo(bus)) {
|
||||
++done;
|
||||
axi_trace_append(AXI_I2C_INT_DONE, done, clear, 0);
|
||||
}
|
||||
|
||||
if (bus->current_todo == 0) {
|
||||
clear |= status & INT_TX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TX FIFO empty or half empty?
|
||||
*/
|
||||
if ((active & (INT_TX_FIFO_EMPTY | INT_TX_FIFO_HALF_FULL)) != 0) {
|
||||
clear |= active & (INT_TX_FIFO_EMPTY | INT_TX_FIFO_HALF_FULL);
|
||||
|
||||
if (!bus->read && !xilinx_axi_i2c_write_tx_fifo(bus)) {
|
||||
++done;
|
||||
axi_trace_append(AXI_I2C_INT_DONE, done, clear, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gate the bus not busy interrupt with the bus busy status to know the bus
|
||||
* is really not busy. It could be an interrupt left over from starting the
|
||||
* transmission.
|
||||
*/
|
||||
if ((active & INT_BUS_NOT_BUSY) != 0) {
|
||||
if ((xilinx_axi_i2c_read_sr(bus) & SR_BB) == 0) {
|
||||
xilinx_axi_i2c_disable_clear_irq(bus, INT_BUS_NOT_BUSY);
|
||||
if (bus->read && !xilinx_axi_i2c_read_rx_fifo(bus)) {
|
||||
++done;
|
||||
axi_trace_append(AXI_I2C_BUS_NOT_BUSY, done, clear, 0);
|
||||
}
|
||||
else if (!bus->read && !xilinx_axi_i2c_write_tx_fifo(bus)) {
|
||||
++done;
|
||||
axi_trace_append(AXI_I2C_BUS_NOT_BUSY, done, clear, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
clear |= INT_BUS_NOT_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (clear != 0)
|
||||
xilinx_axi_i2c_clear_irq(bus, clear);
|
||||
|
||||
if (done != 0) {
|
||||
xilinx_axi_i2c_disable_interrupts(bus);
|
||||
xilinx_axi_i2c_clear_cr(bus, CR_EN);
|
||||
xilinx_axi_i2c_wakeup(bus);
|
||||
}
|
||||
}
|
||||
|
||||
static int xilinx_axi_i2c_transfer(i2c_bus* base,
|
||||
i2c_msg* msgs,
|
||||
uint32_t msg_count)
|
||||
{
|
||||
xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus *) base;
|
||||
rtems_status_code sc;
|
||||
int r = 0;
|
||||
|
||||
axi_trace_reset();
|
||||
axi_trace_append(AXI_I2C_BEGIN, msg_count, 0, 0);
|
||||
|
||||
drv_printk("axi-i2c: i2c transfer\n");
|
||||
|
||||
_Assert(msg_count > 0);
|
||||
|
||||
bus->msgs = &msgs[0];
|
||||
bus->msgs_remaining = msg_count;
|
||||
bus->irqstatus = 0;
|
||||
bus->task_id = rtems_task_self();
|
||||
|
||||
xilinx_axi_i2c_reinit(bus);
|
||||
xilinx_axi_i2c_start_transfer(bus);
|
||||
xilinx_axi_i2c_enable_interrupts(bus);
|
||||
|
||||
sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
axi_trace_append(AXI_I2C_TIMEOUT, 0, 0, 0);
|
||||
xilinx_axi_i2c_reinit(bus);
|
||||
rtems_event_transient_clear();
|
||||
r = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (r == 0 && bus->irqstatus != 0)
|
||||
r = -EIO;
|
||||
|
||||
axi_trace_append(AXI_I2C_END, bus->irqstatus, r, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int xilinx_axi_i2c_set_clock(i2c_bus *base, unsigned long clock)
|
||||
{
|
||||
xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus*) base;
|
||||
|
||||
if ((bus->timing.valid_mask & XILINX_AIX_I2C_AXI_CLOCK) == 0)
|
||||
return -EIO;
|
||||
|
||||
bus->timing.THIGH =
|
||||
(bus->timing.AXI_CLOCK / (2 * clock)) - 7 - bus->timing.SCL_INERTIAL_DELAY;
|
||||
bus->timing.TLOW = bus->timing.THIGH;
|
||||
|
||||
bus->timing.valid_mask |= XILINX_AIX_I2C_THIGH | XILINX_AIX_I2C_TLOW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xilinx_axi_i2c_destroy(i2c_bus* base)
|
||||
{
|
||||
xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus*) base;
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_interrupt_handler_remove(bus->irq, xilinx_axi_i2c_interrupt, bus);
|
||||
_Assert(sc == RTEMS_SUCCESSFUL);
|
||||
(void) sc;
|
||||
|
||||
i2c_bus_destroy_and_free(&bus->base);
|
||||
}
|
||||
|
||||
int
|
||||
i2c_bus_register_xilinx_aix_i2c(const char* bus_path,
|
||||
uintptr_t register_base,
|
||||
rtems_vector_number irq,
|
||||
bool gpo_address,
|
||||
const xilinx_aix_i2c_timing* timing)
|
||||
{
|
||||
xilinx_axi_i2c_bus* bus;
|
||||
rtems_status_code sc;
|
||||
|
||||
bus = (xilinx_axi_i2c_bus*) i2c_bus_alloc_and_init(sizeof(*bus));
|
||||
if (bus == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bus->regs = register_base;
|
||||
bus->irq = irq;
|
||||
bus->gpo_address = gpo_address;
|
||||
bus->timing = *timing;
|
||||
|
||||
sc = rtems_interrupt_handler_install(irq,
|
||||
"Xilinx AXI I2C",
|
||||
RTEMS_INTERRUPT_UNIQUE,
|
||||
xilinx_axi_i2c_interrupt,
|
||||
bus);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
drv_printk("axi-i2c: interrupt attach failed\n");
|
||||
(*bus->base.destroy)(&bus->base);
|
||||
rtems_set_errno_and_return_minus_one(EIO);
|
||||
}
|
||||
|
||||
bus->base.transfer = xilinx_axi_i2c_transfer;
|
||||
bus->base.set_clock = xilinx_axi_i2c_set_clock;
|
||||
bus->base.destroy = xilinx_axi_i2c_destroy;
|
||||
|
||||
axi_i2c_bus = bus;
|
||||
|
||||
return i2c_bus_register(&bus->base, bus_path);
|
||||
}
|
||||
86
cpukit/dev/include/dev/i2c/xilinx-axi-i2c.h
Normal file
86
cpukit/dev/include/dev/i2c/xilinx-axi-i2c.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org> All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xilinx AXI IIC Interface v2.0. See PG090.pdf.
|
||||
*
|
||||
* Note, only master support is provided and no dynamic mode by design.
|
||||
*
|
||||
* The clock set up is to be handled by the IP integrator. There are too many
|
||||
* factors handling this in software.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef XILINX_AXI_I2C_H
|
||||
#define XILINX_AXI_I2C_H
|
||||
|
||||
#include <dev/i2c/i2c.h>
|
||||
|
||||
/*
|
||||
* The PL integrator controls the timing. This interface allows software to
|
||||
* override those settings. It pays to check the timing with ChipScope.
|
||||
*
|
||||
* If you set the AXI bus frequency you can use the clock speed ioctl call to
|
||||
* change the speed dymanically. The ioctl call overrides the defaults passed
|
||||
* in.
|
||||
*
|
||||
* Set the valid mask to the values that are to be set.
|
||||
*/
|
||||
#define XILINX_AIX_I2C_AXI_CLOCK (1 << 0)
|
||||
#define XILINX_AIX_I2C_TSUSTA (1 << 1)
|
||||
#define XILINX_AIX_I2C_TSUSTO (1 << 2)
|
||||
#define XILINX_AIX_I2C_THDSTA (1 << 3)
|
||||
#define XILINX_AIX_I2C_TSUDAT (1 << 4)
|
||||
#define XILINX_AIX_I2C_TBUF (1 << 5)
|
||||
#define XILINX_AIX_I2C_THIGH (1 << 6)
|
||||
#define XILINX_AIX_I2C_TLOW (1 << 7)
|
||||
#define XILINX_AIX_I2C_THDDAT (1 << 8)
|
||||
#define XILINX_AIX_I2C_ALL_REGS (XILINX_AIX_I2C_TSUSTA | \
|
||||
XILINX_AIX_I2C_TSUSTO | \
|
||||
XILINX_AIX_I2C_THDSTA | \
|
||||
XILINX_AIX_I2C_TSUDAT | \
|
||||
XILINX_AIX_I2C_TBUF | \
|
||||
XILINX_AIX_I2C_THIGH | \
|
||||
XILINX_AIX_I2C_TLOW | \
|
||||
XILINX_AIX_I2C_THDDAT)
|
||||
typedef struct
|
||||
{
|
||||
uint32_t valid_mask;
|
||||
uint32_t AXI_CLOCK;
|
||||
uint32_t SCL_INERTIAL_DELAY;
|
||||
uint32_t TSUSTA;
|
||||
uint32_t TSUSTO;
|
||||
uint32_t THDSTA;
|
||||
uint32_t TSUDAT;
|
||||
uint32_t TBUF;
|
||||
uint32_t THIGH;
|
||||
uint32_t TLOW;
|
||||
uint32_t THDDAT;
|
||||
} xilinx_aix_i2c_timing;
|
||||
|
||||
/*
|
||||
* Register the driver.
|
||||
*
|
||||
* The driver can multipex a number of I2C buses (in master mode only) using
|
||||
* the GPO port. The PL designer can use the output pins to select a bus. This
|
||||
* is useful if connecting a number of slave devices that have limit selectable
|
||||
* addresses.
|
||||
*
|
||||
* @param bus_path The driver's device path.
|
||||
* @param register_base AXI base address.
|
||||
* @param irq AXI FPGA interrupt.
|
||||
* @param gpio_address Bits 12:15 of a slave address it written to the GPO.
|
||||
* @param timing Override the default timing. NULL means no changes.
|
||||
*/
|
||||
int i2c_bus_register_xilinx_aix_i2c(const char* bus_path,
|
||||
uintptr_t register_base,
|
||||
rtems_vector_number irq,
|
||||
bool ten_gpio,
|
||||
const xilinx_aix_i2c_timing* timing);
|
||||
|
||||
#endif
|
||||
@@ -39,6 +39,10 @@ $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h: include/dev/i2c/switch-nxp-pca
|
||||
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
|
||||
PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
|
||||
|
||||
$(PROJECT_INCLUDE)/dev/i2c/xilinx-axi-i2c.h: include/dev/i2c/xilinx-axi-i2c.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
|
||||
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/xilinx-axi-i2c.h
|
||||
PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/xilinx-axi-i2c.h
|
||||
|
||||
$(PROJECT_INCLUDE)/dev/spi/$(dirstamp):
|
||||
@$(MKDIR_P) $(PROJECT_INCLUDE)/dev/spi
|
||||
@: > $(PROJECT_INCLUDE)/dev/spi/$(dirstamp)
|
||||
|
||||
Reference in New Issue
Block a user