From 55460652d39a6e60562bd318f35b757d4bf41d5c Mon Sep 17 00:00:00 2001 From: "luohui2320@gmail.com" Date: Tue, 22 May 2012 17:32:32 +0000 Subject: [PATCH] ADD I2C driver framework and I2C gpio driver git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2128 bbd45198-f89e-11dd-88c7-29a3b14d5316 --- components/drivers/i2c/SConscript | 18 ++ components/drivers/i2c/i2c-bit-ops.c | 445 +++++++++++++++++++++++++++ components/drivers/i2c/i2c-bit-ops.h | 41 +++ components/drivers/i2c/i2c.h | 117 +++++++ components/drivers/i2c/i2c_core.c | 373 ++++++++++++++++++++++ components/drivers/i2c/i2c_dev.c | 137 +++++++++ components/drivers/i2c/i2c_dev.h | 28 ++ 7 files changed, 1159 insertions(+) create mode 100644 components/drivers/i2c/SConscript create mode 100644 components/drivers/i2c/i2c-bit-ops.c create mode 100644 components/drivers/i2c/i2c-bit-ops.h create mode 100644 components/drivers/i2c/i2c.h create mode 100644 components/drivers/i2c/i2c_core.c create mode 100644 components/drivers/i2c/i2c_dev.c create mode 100644 components/drivers/i2c/i2c_dev.h diff --git a/components/drivers/i2c/SConscript b/components/drivers/i2c/SConscript new file mode 100644 index 000000000..7a1d5dfe0 --- /dev/null +++ b/components/drivers/i2c/SConscript @@ -0,0 +1,18 @@ +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +src = Split(""" +i2c_core.c +i2c_dev.c +""") + +if GetDepend('RT_USING_I2C_BITOPS'): + src = src + ['i2c-bit-ops.c'] + +# The set of source files associated with this SConscript file. +path = [cwd] + +group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_I2C'], CPPPATH = path) + +Return('group') diff --git a/components/drivers/i2c/i2c-bit-ops.c b/components/drivers/i2c/i2c-bit-ops.c new file mode 100644 index 000000000..6d87df46e --- /dev/null +++ b/components/drivers/i2c/i2c-bit-ops.c @@ -0,0 +1,445 @@ +/* + * File : i2c-bit-ops.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2012-04-25 weety first version + */ + +#include +#include + +#ifdef RT_I2C_BIT_DEBUG +#define bit_dbg(fmt, ...) rt_kprintf(fmt, ##__VA_ARGS__) +#else +#define bit_dbg(fmt, ...) +#endif + + +#define SET_SDA(ops, val) ops->set_sda(ops->data, val) +#define SET_SCL(ops, val) ops->set_scl(ops->data, val) +#define GET_SDA(ops) ops->get_sda(ops->data) +#define GET_SCL(ops) ops->get_scl(ops->data) + +rt_inline void i2c_delay(struct rt_i2c_bit_ops *ops) +{ + ops->udelay((ops->delay_us + 1) >> 1); +} + +rt_inline void i2c_delay2(struct rt_i2c_bit_ops *ops) +{ + ops->udelay(ops->delay_us); +} + +#define SDA_L(ops) SET_SDA(ops, 0) +#define SDA_H(ops) SET_SDA(ops, 1) +#define SCL_L(ops) SET_SCL(ops, 0) + +/* + * release scl line, and wait scl line to high. + */ +static rt_err_t SCL_H(struct rt_i2c_bit_ops *ops) +{ + rt_tick_t start; + + SET_SCL(ops, 1); + + if (!ops->get_scl) + goto done; + + start = rt_tick_get(); + while (!GET_SCL(ops)) + { + if ((rt_tick_get() - start) > ops->timeout) + return -RT_ETIMEOUT; + rt_thread_delay((ops->timeout + 1) >> 1); + } +#ifdef RT_I2C_BIT_DEBUG + if (rt_tick_get() != start) + { + bit_dbg("wait %ld tick for SCL line to go high\n", + rt_tick_get() - start); + } +#endif + +done: + i2c_delay(ops); + + return RT_EOK; +} + + +static void i2c_start(struct rt_i2c_bit_ops *ops) +{ +#ifdef RT_I2C_BIT_DEBUG + if (ops->get_scl && !GET_SCL(ops)) + { + bit_dbg("I2C bus error, SCL line low\n"); + } + if (ops->get_sda && !GET_SDA(ops)) + { + bit_dbg("I2C bus error, SDA line low\n"); + } +#endif + SDA_L(ops); + i2c_delay(ops); + SCL_L(ops); +} + +static void i2c_restart(struct rt_i2c_bit_ops *ops) +{ + SDA_H(ops); + SCL_H(ops); + i2c_delay(ops); + SDA_L(ops); + i2c_delay(ops); + SCL_L(ops); +} + + +static void i2c_stop(struct rt_i2c_bit_ops *ops) +{ + SDA_L(ops); + i2c_delay(ops); + SCL_H(ops); + i2c_delay(ops); + SDA_H(ops); + i2c_delay2(ops); +} + +rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops) +{ + rt_bool_t ack; + + SDA_H(ops); + i2c_delay(ops); + + if (SCL_H(ops) < 0) + { + bit_dbg("wait ack timeout\n"); + return -RT_ETIMEOUT; + } + + ack = !GET_SDA(ops); /* ack : sda pin is pulled low */ + bit_dbg("%s\n", ack ? "ACK" : "NACK"); + + SCL_L(ops); + + return ack; +} + + +static rt_int32_t i2c_writeb(struct rt_i2c_bus *bus, rt_uint8_t data) +{ + rt_int32_t i; + rt_uint8_t bit; + + struct rt_i2c_bit_ops *ops = bus->priv; + + for (i = 7; i >= 0; i--) + { + SCL_L(ops); + bit = (data >> i) & 1; + SET_SDA(ops, bit); + i2c_delay(ops); + if (SCL_H(ops) < 0) + { + bit_dbg("i2c_writeb: 0x%02x, " + "wait scl pin high timeout at bit #%d\n", + data, i); + return -RT_ETIMEOUT; + } + + } + SCL_L(ops); + i2c_delay(ops); + + return i2c_waitack(ops); +} + + +static rt_int32_t i2c_readb(struct rt_i2c_bus *bus) +{ + rt_uint8_t i; + rt_uint8_t data = 0; + struct rt_i2c_bit_ops *ops = bus->priv; + + SDA_H(ops); + i2c_delay(ops); + for (i = 0; i < 8; i++) + { + data <<= 1; + + if (SCL_H(ops) < 0) + { + bit_dbg("i2c_readb: wait scl pin high " + "timeout at bit #%d\n", 7 - i); + return -RT_ETIMEOUT; + } + + if (GET_SDA(ops)) + data |= 1; + SCL_L(ops); + i2c_delay2(ops); + } + + return data; +} + + +static rt_size_t i2c_send_bytes(struct rt_i2c_bus *bus, struct rt_i2c_msg *msg) +{ + rt_int32_t ret; + rt_size_t bytes = 0; + const rt_uint8_t *ptr = msg->buf; + rt_int32_t count = msg->len; + rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + + while (count > 0) + { + ret = i2c_writeb(bus, *ptr); + + if ((ret > 0) || (ignore_nack && (ret == 0))) + { + count--; + ptr++; + bytes++; + } + else if (ret == 0) + { + rt_kprintf("send bytes: NACK.\n"); + return -RT_ERROR; + } + else + { + rt_kprintf("send bytes: error %d\n", ret); + return ret; + } + } + return bytes; +} + +static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus *bus, int ack) +{ + struct rt_i2c_bit_ops *ops = bus->priv; + + if (ack) + SET_SDA(ops, 0); + i2c_delay(ops); + if (SCL_H(ops) < 0) + { + rt_kprintf("ACK or NACK timeout\n"); + return -RT_ETIMEOUT; + } + SCL_L(ops); + return RT_EOK; +} + +static rt_size_t i2c_recv_bytes(struct rt_i2c_bus *bus, struct rt_i2c_msg *msg) +{ + rt_int32_t val; + rt_int32_t bytes = 0; /* actual bytes */ + rt_uint8_t *ptr = msg->buf; + rt_int32_t count = msg->len; + const rt_uint32_t flags = msg->flags; + + while (count > 0) + { + val = i2c_readb(bus); + if (val >= 0) + { + *ptr = val; + bytes++; + } + else + { + break; + } + + ptr++; + count--; + + bit_dbg("recieve bytes: 0x%02x, %s\n", + val, (flags & RT_I2C_NO_READ_ACK) ? + "(No ACK/NACK)" : (count ? "ACK" : "NACK")); + + if (!(flags & RT_I2C_NO_READ_ACK)) + { + val = i2c_send_ack_or_nack(bus, count); + if (val < 0) + return val; + } + } + return bytes; +} + +static rt_int32_t i2c_send_address(struct rt_i2c_bus *bus, + rt_uint8_t addr, rt_int32_t retries) +{ + struct rt_i2c_bit_ops *ops = bus->priv; + rt_int32_t i; + rt_err_t ret = 0; + + for (i = 0; i <= retries; i++) + { + ret = i2c_writeb(bus, addr); + if (ret == 1 || i == retries) + break; + bit_dbg("send stop condition\n"); + i2c_stop(ops); + i2c_delay2(ops); + bit_dbg("send start condition\n"); + i2c_start(ops); + } + + return ret; +} + +static rt_err_t i2c_bit_send_address(struct rt_i2c_bus *bus, struct rt_i2c_msg *msg) +{ + rt_uint16_t flags = msg->flags; + rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + struct rt_i2c_bit_ops *ops = bus->priv; + + rt_uint8_t addr1, addr2; + rt_int32_t retries; + rt_err_t ret; + + retries = ignore_nack ? 0 : bus->retries; + + if (flags & RT_I2C_ADDR_10BIT) + { + addr1 = 0xf0 | ((msg->addr >> 7) & 0x06); + addr2 = msg->addr & 0xff; + + bit_dbg("addr1: %d, addr2: %d\n", addr1, addr2); + + ret = i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + { + rt_kprintf("NACK: sending first addr\n"); + return -RT_EIO; + } + + ret = i2c_writeb(bus, addr2); + if ((ret != 1) && !ignore_nack) + { + rt_kprintf("NACK: sending second addr\n"); + return -RT_EIO; + } + if (flags & RT_I2C_RD) + { + bit_dbg("send repeated start condition\n"); + i2c_restart(ops); + addr1 |= 0x01; + ret = i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + { + rt_kprintf("NACK: sending repeated addr\n"); + return -RT_EIO; + } + } + } + else + { /* 7-bit addr */ + addr1 = msg->addr << 1; + if (flags & RT_I2C_RD) + addr1 |= 1; + ret = i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + return -RT_EIO; + } + + return RT_EOK; +} + + +static rt_size_t i2c_bit_xfer(struct rt_i2c_bus *bus, + struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + struct rt_i2c_msg *msg; + struct rt_i2c_bit_ops *ops = bus->priv; + rt_int32_t i, ret; + rt_uint16_t ignore_nack; + + bit_dbg("send start condition\n"); + i2c_start(ops); + for (i = 0; i < num; i++) + { + msg = &msgs[i]; + ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + if (!(msg->flags & RT_I2C_NO_START)) + { + if (i) + { + i2c_restart(ops); + } + ret = i2c_bit_send_address(bus, msg); + if ((ret != RT_EOK) && !ignore_nack) + { + bit_dbg("receive NACK from device addr 0x%02x msg #%d\n", + msgs[i].addr, i); + goto out; + } + } + if (msg->flags & RT_I2C_RD) + { + ret = i2c_recv_bytes(bus, msg); + if (ret >= 1) + bit_dbg("read %d byte%s\n", + ret, ret == 1 ? "" : "s"); + if (ret < msg->len) + { + if (ret >= 0) + ret = -RT_EIO; + goto out; + } + } + else + { + ret = i2c_send_bytes(bus, msg); + if (ret >= 1) + bit_dbg("write %d byte%s\n", + ret, ret == 1 ? "" : "s"); + if (ret < msg->len) + { + if (ret >= 0) + ret = -RT_ERROR; + goto out; + } + } + } + ret = i; + +out: + bit_dbg("send stop condition\n"); + i2c_stop(ops); + + return ret; +} + + +static const struct rt_i2c_bus_ops i2c_bit_bus_ops = { + i2c_bit_xfer, + RT_NULL, + RT_NULL +}; + + +rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus *bus) +{ + rt_err_t err; + + struct rt_i2c_bit_ops *bit_ops = bus->priv; + RT_ASSERT(bit_ops != RT_NULL); + + bus->ops = &i2c_bit_bus_ops; + + return rt_i2c_bus_register(bus); +} diff --git a/components/drivers/i2c/i2c-bit-ops.h b/components/drivers/i2c/i2c-bit-ops.h new file mode 100644 index 000000000..0f4947863 --- /dev/null +++ b/components/drivers/i2c/i2c-bit-ops.h @@ -0,0 +1,41 @@ +/* + * File : i2c-bit-ops.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2012-04-25 weety first version + */ + +#ifndef __I2C_BIT_OPS_H__ +#define __I2C_BIT_OPS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rt_i2c_bit_ops { + void *data; /* private data for lowlevel routines */ + void (*set_sda) (void *data, rt_int32_t state); + void (*set_scl) (void *data, rt_int32_t state); + rt_int32_t (*get_sda) (void *data); + rt_int32_t (*get_scl) (void *data); + + void (*udelay) (rt_uint32_t us); + + rt_uint32_t delay_us; /* scl and sda line delay */ + rt_uint32_t timeout; /* in tick */ +}; + +rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus *bus); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/drivers/i2c/i2c.h b/components/drivers/i2c/i2c.h new file mode 100644 index 000000000..987e720b2 --- /dev/null +++ b/components/drivers/i2c/i2c.h @@ -0,0 +1,117 @@ +/* + * File : i2c.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2012-04-25 weety first version + */ + +#ifndef __I2C_H__ +#define __I2C_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef RT_I2C_NAME_SIZE +#define RT_I2C_NAME_SIZE 32 +#endif + +#define RT_I2C_WR 0x0000 +#define RT_I2C_RD (1u << 0) +#define RT_I2C_ADDR_10BIT (1u << 2) /* this is a ten bit chip address */ +#define RT_I2C_NO_START (1u << 4) +#define RT_I2C_IGNORE_NACK (1u << 5) +#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */ + +struct rt_i2c_msg { + rt_uint16_t addr; + rt_uint16_t flags; + rt_uint16_t len; + rt_uint8_t *buf; +}; + +struct rt_i2c_hardware_info { + char name[RT_I2C_NAME_SIZE]; + rt_uint16_t flags; + rt_uint16_t addr; + rt_uint32_t bus_id; + rt_list_t list; +}; + +#define RT_I2C_HARDWARE_INFO(name, flags, addr, bus_id) \ + name,flags,addr,bus_id,{RT_NULL,RT_NULL} + +struct rt_i2c_bus; + +struct rt_i2c_bus_ops { + rt_size_t (*master_xfer) (struct rt_i2c_bus *bus, struct rt_i2c_msg *msgs, rt_uint32_t num); + rt_size_t (*slave_xfer) (struct rt_i2c_bus *bus, struct rt_i2c_msg *msgs, rt_uint32_t num); + rt_err_t (*i2c_bus_control) (struct rt_i2c_bus *bus, rt_uint32_t, rt_uint32_t); +}; + +/*for i2c bus driver*/ +struct rt_i2c_bus { + struct rt_device *parent; + char name[RT_I2C_NAME_SIZE]; + rt_uint32_t id; + const struct rt_i2c_bus_ops *ops; + struct rt_mutex lock; + rt_list_t devices; + rt_list_t list; + rt_uint32_t timeout; + rt_uint32_t retries; + void *priv; +}; + +struct rt_i2c_device; +struct rt_i2c_driver { + char name[RT_I2C_NAME_SIZE]; + rt_err_t (*probe)(struct rt_i2c_device *device); + rt_err_t (*remove)(struct rt_i2c_device *device); + rt_list_t devices; +}; + +/*for i2c device driver*/ +struct rt_i2c_device { + rt_uint32_t flags; + rt_uint16_t addr; + struct rt_i2c_bus *bus; + struct rt_i2c_driver *driver; + struct rt_device dev; + rt_list_t drv_list; + rt_list_t bus_list; +}; + +#ifdef RT_I2C_DEBUG +#define i2c_dbg(fmt, ...) rt_kprintf(fmt, ##__VA_ARGS__) +#else +#define i2c_dbg(fmt, ...) +#endif + +rt_err_t rt_i2c_bus_register(struct rt_i2c_bus *bus); +rt_err_t rt_i2c_bus_unregister(struct rt_i2c_bus *bus); +void rt_i2c_hw_info_register(struct rt_i2c_hardware_info *info, rt_uint32_t size); +rt_err_t rt_i2c_bus_attach_driver(struct rt_i2c_driver *driver); +rt_err_t rt_i2c_bus_detach_driver(struct rt_i2c_driver *driver); +rt_size_t rt_i2c_transfer(struct rt_i2c_bus *bus, + struct rt_i2c_msg *msgs, rt_uint32_t size); +rt_size_t rt_i2c_master_send(struct rt_i2c_device *device, + const rt_uint8_t *buf, rt_uint32_t size); +rt_size_t rt_i2c_master_recv(struct rt_i2c_device *device, + rt_uint8_t *buf ,rt_uint32_t size); +rt_err_t rt_i2c_core_init(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/drivers/i2c/i2c_core.c b/components/drivers/i2c/i2c_core.c new file mode 100644 index 000000000..e48b2e494 --- /dev/null +++ b/components/drivers/i2c/i2c_core.c @@ -0,0 +1,373 @@ +/* + * File : i2c_core.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2012-04-25 weety first version + */ + +#include +#include + +static struct rt_mutex i2c_core_lock; +static struct rt_mutex i2c_hardware_lock; +static rt_list_t i2c_hw_info_list = RT_LIST_OBJECT_INIT(i2c_hw_info_list); +static rt_list_t i2c_bus_list = RT_LIST_OBJECT_INIT(i2c_bus_list); + +static struct rt_i2c_bus *find_i2c_bus(rt_uint32_t id) +{ + rt_list_t *list; + struct rt_i2c_bus *bus = RT_NULL; + + for (list = (&i2c_bus_list)->next; list != &i2c_bus_list; list = list->next) + { + bus = (struct rt_i2c_bus *)rt_list_entry(list, struct rt_i2c_bus, list); + if (bus->id == id) + { + return bus; + } + } + + return RT_NULL; +} + + +rt_err_t rt_i2c_bus_register(struct rt_i2c_bus *bus) +{ + rt_err_t ret = RT_EOK; + struct rt_i2c_bus *tbus; + + rt_mutex_init (&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_FIFO); + + rt_mutex_take(&i2c_core_lock, RT_WAITING_FOREVER); + + tbus = find_i2c_bus(bus->id); + if (tbus != RT_NULL) + { + rt_kprintf("I2C bus ID [%d] already registered\n", bus->id); + ret = -RT_ERROR; + goto out; + } + + if (bus->timeout == 0) + bus->timeout = RT_TICK_PER_SECOND; + + rt_list_init(&bus->devices); + + ret = rt_i2c_bus_device_init(bus, bus->name); + if (ret != RT_EOK) + { + rt_mutex_release(&i2c_core_lock); + rt_kprintf("I2C bus [%s] register failed\n", bus->name); + goto out; + } + + rt_list_insert_after(&i2c_bus_list, &bus->list); + + rt_mutex_release(&i2c_core_lock); + + rt_kprintf("I2C bus [%s] registered\n", bus->name); +out: + return ret; +} + + +rt_err_t rt_i2c_bus_unregister(struct rt_i2c_bus *bus) +{ + rt_err_t ret = RT_EOK; + struct rt_i2c_bus *bus_l; + rt_list_t *list; + struct rt_i2c_device *device; + + rt_mutex_take(&i2c_core_lock, RT_WAITING_FOREVER); + + rt_i2c_bus_device_exit(bus); + + for (list = (&bus->devices)->next; list != &bus->devices; list = list->next) + { + device = (struct rt_i2c_device *)rt_list_entry(list, struct rt_i2c_device, bus_list); + if (device) + { + ret = device->driver->remove(device); + if (ret != RT_EOK) + { + i2c_dbg("I2C driver [%s] unregister failed\n", device->driver->name); + goto out; + } + rt_list_remove(&device->drv_list); + rt_list_remove(&device->bus_list); + } + } + + rt_list_init(&bus->devices); + rt_list_remove(&bus->list); + + rt_mutex_detach (&bus->lock); + + rt_kprintf("I2C bus [%s] unregister\n", bus->name); +out: + rt_mutex_release(&i2c_core_lock); + return ret; +} + + +rt_inline struct rt_i2c_hardware_info * +i2c_check_hw_info(struct rt_i2c_hardware_info *hwinfo) +{ + rt_list_t *list; + struct rt_i2c_hardware_info *info; + + for (list = (&i2c_hw_info_list)->next; list != &i2c_hw_info_list; list = list->next) + { + info = (struct rt_i2c_hardware_info *)rt_list_entry(list, + struct rt_i2c_hardware_info, list); + if ((info->bus_id == hwinfo->bus_id) && (info->addr == hwinfo->addr)) + { + return info; + } + } + + return RT_NULL; +} + +void rt_i2c_hw_info_register(struct rt_i2c_hardware_info *info, rt_uint32_t size) +{ + rt_mutex_take(&i2c_hardware_lock, RT_WAITING_FOREVER); + for( ; size > 0; size--, info++) + { + if (i2c_check_hw_info(info) == RT_NULL) + { + rt_list_insert_after(&i2c_hw_info_list, &info->list); + } + else + { + rt_kprintf("I2C hw info [%s:%d:%d] already registered\n", + info->name, info->bus_id, info->addr); + } + } + rt_mutex_release(&i2c_hardware_lock); +} + +rt_err_t rt_i2c_check_addr(struct rt_i2c_bus *bus, rt_uint16_t addr) +{ + rt_list_t *list; + struct rt_i2c_device *device; + + for (list = (&bus->devices)->next; list != &bus->devices; list = list->next) + { + device = (struct rt_i2c_device *)rt_list_entry(list, struct rt_i2c_device, bus_list); + if (device->addr == addr) + { + rt_kprintf("ERR: device at addr[0x%02x] " + "already registered\n", addr); + return -RT_ERROR; + } + } + + return RT_EOK; +} + +static rt_err_t i2c_driver_probe(struct rt_i2c_bus *bus, + struct rt_i2c_driver *driver, struct rt_i2c_hardware_info *info) +{ + rt_err_t ret = RT_EOK; + struct rt_i2c_device *device; + + device = rt_malloc(sizeof(struct rt_i2c_device)); + if (device == RT_NULL) + { + i2c_dbg("I2C malloc memory failed\n"); + return -RT_ENOMEM; + } + rt_memset(device, 0, sizeof(struct rt_i2c_device)); + + device->flags = info->flags; + device->addr = info->addr; + device->bus = bus; + device->driver = driver; + + rt_list_insert_after(&bus->devices, &device->bus_list); + rt_list_insert_after(&driver->devices, &device->drv_list); + ret = driver->probe(device); + + return ret; +} + +static rt_err_t i2c_bus_match_hw(struct rt_i2c_driver *driver) +{ + rt_err_t ret = RT_EOK; + rt_list_t *list; + struct rt_i2c_hardware_info *info; + struct rt_i2c_bus *bus = RT_NULL; + + for (list = (&i2c_hw_info_list)->next; list != &i2c_hw_info_list; list = list->next) + { + info = (struct rt_i2c_hardware_info *)rt_list_entry(list, + struct rt_i2c_hardware_info, list); + if (rt_strncmp(info->name, driver->name, RT_I2C_NAME_SIZE) == 0) + { + bus = find_i2c_bus(info->bus_id); + if (bus) + { + if (rt_i2c_check_addr(bus, info->addr) != RT_EOK) + { + continue; + } + if (i2c_driver_probe(bus, driver, info) != RT_EOK) + { + ret = -RT_ERROR; + } + } + } + } + + return ret; +} + +rt_err_t rt_i2c_bus_attach_driver(struct rt_i2c_driver *driver) +{ + rt_err_t ret = RT_EOK; + + rt_mutex_take(&i2c_core_lock, RT_WAITING_FOREVER); + ret = i2c_bus_match_hw(driver); + rt_mutex_release(&i2c_core_lock); + + if (ret != RT_EOK) + { + goto out; + } + + rt_kprintf("I2C driver [%s] registered\n", driver->name); + + return RT_EOK; + +out: + rt_kprintf("I2C driver [%s] register failed\n", driver->name); + return ret; +} + + +rt_err_t rt_i2c_bus_detach_driver(struct rt_i2c_driver *driver) +{ + rt_err_t ret = RT_EOK; + rt_list_t *list; + struct rt_i2c_device *device; + + rt_mutex_take(&i2c_core_lock, RT_WAITING_FOREVER); + for (list = (&driver->devices)->next; list != &driver->devices; list = list->next) + { + device = (struct rt_i2c_device *)rt_list_entry(list, struct rt_i2c_device, drv_list); + if (device) + { + ret = driver->remove(device); + if (ret != RT_EOK) + { + rt_mutex_release(&i2c_core_lock); + goto out; + } + rt_list_remove(&device->drv_list); + rt_list_remove(&device->bus_list); + } + } + + rt_mutex_release(&i2c_core_lock); + + rt_kprintf("I2C driver [%s] unregister\n", driver->name); + +out: + return ret; +} + + +rt_size_t rt_i2c_transfer(struct rt_i2c_bus *bus, + struct rt_i2c_msg *msgs, rt_uint32_t size) +{ + rt_size_t ret; + + if (bus->ops->master_xfer) + { +#ifdef RT_I2C_DEBUG + for (ret = 0; ret < size; ret++) + { + i2c_dbg("msgs[%d] %c, addr=0x%02x, len=%d%s\n", ret, + (msgs[ret].flags & RT_I2C_RD) ? 'R' : 'W', + msgs[ret].addr, msgs[ret].len); + } +#endif + + rt_mutex_take(&bus->lock, RT_WAITING_FOREVER); + + ret = bus->ops->master_xfer(bus, msgs, size); + rt_mutex_release(&bus->lock); + + return ret; + } + else + { + rt_kprintf("I2C bus transfers not supported\n"); + return -RT_ERROR; + } +} + + +rt_size_t rt_i2c_master_send(struct rt_i2c_device *device, + const rt_uint8_t *buf, rt_uint32_t size) +{ + rt_size_t ret; + struct rt_i2c_msg msg; + struct rt_i2c_bus *bus = device->bus; + + msg.addr = device->addr; + msg.flags = device->flags & RT_I2C_ADDR_10BIT; + msg.len = size; + msg.buf = (rt_uint8_t *)buf; + + ret = rt_i2c_transfer(bus, &msg, 1); + + if (ret > 0) + { + return size; + } + + return ret; +} + + + +rt_size_t rt_i2c_master_recv(struct rt_i2c_device *device, + rt_uint8_t *buf, rt_uint32_t size) +{ + rt_size_t ret; + struct rt_i2c_msg msg; + struct rt_i2c_bus *bus = device->bus; + RT_ASSERT(bus != RT_NULL); + + msg.addr = device->addr; + msg.flags = device->flags & RT_I2C_ADDR_10BIT; + msg.flags |= RT_I2C_RD; + msg.len = size; + msg.buf = buf; + + ret = rt_i2c_transfer(bus, &msg, 1); + + if (ret > 0) + { + return size; + } + + return ret; +} + + +rt_err_t rt_i2c_core_init() +{ + rt_mutex_init (&i2c_core_lock, "i2c_core_lock", RT_IPC_FLAG_FIFO); + rt_mutex_init (&i2c_hardware_lock, "i2c_hw_lock", RT_IPC_FLAG_FIFO); +} + diff --git a/components/drivers/i2c/i2c_dev.c b/components/drivers/i2c/i2c_dev.c new file mode 100644 index 000000000..e74d6252a --- /dev/null +++ b/components/drivers/i2c/i2c_dev.c @@ -0,0 +1,137 @@ +#include +#include + +static rt_err_t i2c_bus_device_init(rt_device_t dev) +{ + struct rt_i2c_bus* bus; + struct rt_i2c_device *i2c_device = dev->user_data; + + bus = i2c_device->bus; + RT_ASSERT(bus != RT_NULL); + + return RT_EOK; +} + +static rt_size_t i2c_bus_device_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) +{ + struct rt_i2c_bus* bus; + struct rt_i2c_device *i2c_device = dev->user_data; + + bus = i2c_device->bus; + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(i2c_device != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + i2c_dbg("I2C bus dev [%s] reading %u bytes.\n", bus->name, count); + + return rt_i2c_master_recv(i2c_device, buffer, count); +} + + +static rt_size_t i2c_bus_device_write (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct rt_i2c_bus* bus; + struct rt_i2c_device *i2c_device = dev->user_data; + + bus = i2c_device->bus; + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(i2c_device != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + i2c_dbg("I2C bus dev writing %u bytes.\n", bus->name, size); + + return rt_i2c_master_send(i2c_device, buffer, size); +} + +static rt_err_t i2c_bus_device_control(rt_device_t dev, rt_uint8_t cmd, void *args) +{ + rt_err_t ret; + struct rt_i2c_bus* bus; + struct rt_i2c_priv_data *priv_data; + struct rt_i2c_device *i2c_device = dev->user_data; + + bus = i2c_device->bus; + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(i2c_device != RT_NULL); + + switch (cmd) + { + case RT_I2C_DEV_CTRL_10BIT: /* set 10-bit addr mode */ + i2c_device->flags |= RT_I2C_ADDR_10BIT; + break; + case RT_I2C_DEV_CTRL_ADDR: + i2c_device->addr = *(rt_uint16_t *)args; + break; + case RT_I2C_DEV_CTRL_TIMEOUT: + bus->timeout = *(rt_uint32_t *)args; + break; + case RT_I2C_DEV_CTRL_RW: + priv_data = (struct rt_i2c_priv_data *)args; + ret = rt_i2c_transfer(bus, priv_data->msgs, priv_data->number); + if (ret < 0) + { + return -RT_EIO; + } + break; + default: break; + } + + return RT_EOK; +} + + +rt_err_t rt_i2c_bus_device_init(struct rt_i2c_bus* bus, const char* name) +{ + struct rt_device *device; + struct rt_i2c_device *i2c_device; + RT_ASSERT(bus != RT_NULL); + + //device = &bus->parent; + + i2c_device = rt_malloc(sizeof(struct rt_i2c_device)); + if (i2c_device == RT_NULL) + { + return -RT_ENOMEM; + } + + rt_memset(i2c_device, 0, sizeof(struct rt_i2c_device)); + + device = &i2c_device->dev; + i2c_device->bus = bus; + bus->parent = device; + + device->user_data = i2c_device; + + /* set device type */ + device->type = RT_Device_Class_I2CBUS; + /* initialize device interface */ + device->init = i2c_bus_device_init; + device->open = RT_NULL; + device->close = RT_NULL; + device->read = i2c_bus_device_read; + device->write = i2c_bus_device_write; + device->control = i2c_bus_device_control; + + /* register to device manager */ + rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +} + +rt_err_t rt_i2c_bus_device_exit(struct rt_i2c_bus* bus) +{ + struct rt_device *device; + struct rt_i2c_device *i2c_device; + RT_ASSERT(bus != RT_NULL); + + device = bus->parent; + + i2c_device = device->user_data;; + + /* register to device manager */ + rt_device_unregister(device); + + rt_free(i2c_device); + + return RT_EOK; +} diff --git a/components/drivers/i2c/i2c_dev.h b/components/drivers/i2c/i2c_dev.h new file mode 100644 index 000000000..5f343929a --- /dev/null +++ b/components/drivers/i2c/i2c_dev.h @@ -0,0 +1,28 @@ +#ifndef __I2C_DEV_H__ +#define __I2C_DEV_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RT_I2C_DEV_CTRL_10BIT 0x20 +#define RT_I2C_DEV_CTRL_ADDR 0x21 +#define RT_I2C_DEV_CTRL_TIMEOUT 0x22 +#define RT_I2C_DEV_CTRL_RW 0x23 + +struct rt_i2c_priv_data { + struct rt_i2c_msg *msgs; + rt_size_t number; +}; + +rt_err_t rt_i2c_bus_device_init(struct rt_i2c_bus* bus, const char* name); +rt_err_t rt_i2c_bus_device_exit(struct rt_i2c_bus* bus); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file