From 2b8c20af4bd02b0244a51585e78fa649d16701e3 Mon Sep 17 00:00:00 2001 From: "bernard.xiong@gmail.com" Date: Sun, 8 Jan 2012 14:16:51 +0000 Subject: [PATCH] add SPI framework. git-svn-id: https://rt-thread.googlecode.com/svn/trunk@1911 bbd45198-f89e-11dd-88c7-29a3b14d5316 --- components/drivers/include/drivers/spi.h | 117 ++++++++++ components/drivers/spi/spi_core.c | 263 +++++++++++++++++++++++ components/drivers/spi/spi_dev.c | 149 +++++++++++++ 3 files changed, 529 insertions(+) create mode 100644 components/drivers/include/drivers/spi.h create mode 100644 components/drivers/spi/spi_core.c create mode 100644 components/drivers/spi/spi_dev.c diff --git a/components/drivers/include/drivers/spi.h b/components/drivers/include/drivers/spi.h new file mode 100644 index 000000000..d7aad13c6 --- /dev/null +++ b/components/drivers/include/drivers/spi.h @@ -0,0 +1,117 @@ +#ifndef __SPI_H__ +#define __SPI_H__ + +#include + +#define RT_SPI_CPHA (1<<0) /* bit[0]:CPHA */ +#define RT_SPI_CPOL (1<<1) /* bit[1]:CPOL */ +#define RT_SPI_LSB (0<<2) /* bit[2]: 0-LSB */ +#define RT_SPI_MSB (1<<2) /* bit[2]: 1-MSB */ + +#define RT_SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */ +#define RT_SPI_MODE_1 (0 | RT_SPI_CPHA) /* CPOL = 0, CPHA = 1 */ +#define RT_SPI_MODE_2 (RT_SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */ +#define RT_SPI_MODE_3 (RT_SPI_CPOL | RT_SPI_CPHA) /* CPOL = 1, CPHA = 1 */ + +#define RT_SPI_MODE_MASK (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB) + +/** + * SPI message structure + */ +struct rt_spi_message +{ + const void* send_buf; + void* recv_buf; + rt_size_t length; + + unsigned cs_take:1; + unsigned cs_release:1; +}; + +/** + * SPI configuration structure + */ +struct rt_spi_configuration +{ + rt_uint8_t mode; + rt_uint8_t data_width; + rt_uint16_t reserved; + + rt_uint32_t max_hz; +}; + +struct rt_spi_ops; +struct rt_spi_bus +{ + struct rt_device parent; + struct rt_spi_ops *ops; + + struct rt_mutex lock; + struct rt_spi_device* owner; +}; + +/** + * SPI operators + */ +struct rt_spi_ops +{ + rt_err_t (*configure)(struct rt_spi_device* device, struct rt_spi_configuration* configuration); + rt_uint32_t (*xfer)(struct rt_spi_device* device, struct rt_spi_message* message); +}; + +/** + * SPI Virtual BUS, one device must connected to a virtual BUS + */ +struct rt_spi_device +{ + struct rt_device parent; + struct rt_spi_bus *bus; + + struct rt_spi_configuration config; +}; +#define SPI_DEVICE(dev) ((struct rt_spi_device*)(dev)) + +/* register a SPI bus */ +rt_err_t rt_spi_bus_register(struct rt_spi_bus* bus, const char* name, struct rt_spi_ops* ops); +/* attach a device on SPI bus */ +rt_err_t rt_spi_bus_attach_device(struct rt_spi_device* device, const char* name, const char* bus_name, void* user_data); +/* set configuration on SPI device */ +rt_err_t rt_spi_configure(struct rt_spi_device* device, struct rt_spi_configuration* cfg); + +/* send data then receive data from SPI devicew */ +rt_err_t rt_spi_send_then_recv(struct rt_spi_device* device, const void *send_buf, rt_size_t send_length, + void* recv_buf, rt_size_t recv_length); + +rt_err_t rt_spi_send_then_send(struct rt_spi_device* device, const void *send_buf1, rt_size_t send_length1, + const void* send_buf2, rt_size_t send_length2); + +rt_size_t rt_spi_transfer(struct rt_spi_device* device, const void *send_buf, + void* recv_buf, rt_size_t length); + +rt_inline rt_size_t rt_spi_recv(struct rt_spi_device* device, void* recv_buf, rt_size_t length) +{ + return rt_spi_transfer(device, RT_NULL, recv_buf, length); +} + +rt_inline rt_size_t rt_spi_send(struct rt_spi_device* device, const void* send_buf, rt_size_t length) +{ + return rt_spi_transfer(device, send_buf, RT_NULL, length); +} + +rt_inline rt_uint8_t rt_spi_sendrecv8(struct rt_spi_device* device, rt_uint8_t data) +{ + rt_uint8_t value; + + rt_spi_send_then_recv(device, &data, 1, &value, 1); + return value; +} + +rt_inline rt_uint16_t rt_spi_sendrecv16(struct rt_spi_device* device, rt_uint16_t data) +{ + rt_uint16_t value; + + rt_spi_send_then_recv(device, &data, 2, &value, 2); + return value; +} + +#endif diff --git a/components/drivers/spi/spi_core.c b/components/drivers/spi/spi_core.c new file mode 100644 index 000000000..d4c265da6 --- /dev/null +++ b/components/drivers/spi/spi_core.c @@ -0,0 +1,263 @@ +#include + +extern rt_err_t rt_spi_bus_device_init(struct rt_spi_bus* bus, const char* name); +extern rt_err_t rt_spidev_device_init(struct rt_spi_device* dev, const char* name); + +rt_err_t rt_spi_bus_register(struct rt_spi_bus* bus, const char* name, struct rt_spi_ops* ops) +{ + rt_err_t result; + + result = rt_spi_bus_device_init(bus, name); + if (result != RT_EOK) return result; + + /* initialize mutex lock */ + rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_FIFO); + /* set ops */ + bus->ops = ops; + /* initialize owner */ + bus->owner = RT_NULL; + + return RT_EOK; +} + +rt_err_t rt_spi_bus_attach_device(struct rt_spi_device* device, const char* name, const char* bus_name, void* user_data) +{ + rt_device_t bus; + + /* get physical spi bus */ + bus = rt_device_find(bus_name); + if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS) + { + device->bus = (struct rt_spi_bus*)bus; + + /* initialize spidev device */ + rt_spidev_device_init(device, name); + rt_memset(&device->config, 0, sizeof(device->config)); + device->parent.user_data = user_data; + } + + /* not found the host bus */ + return -RT_ERROR; +} + +rt_err_t rt_spi_configure(struct rt_spi_device* device, struct rt_spi_configuration* cfg) +{ + rt_err_t result; + + RT_ASSERT(device != RT_NULL); + + /* set configuration */ + device->config.data_width = cfg->data_width; + device->config.mode = cfg->mode & RT_SPI_MODE_MASK ; + device->config.max_hz = cfg->max_hz ; + + if (device->bus != RT_NULL) + { + result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (device->bus->owner == device) + { + device->bus->ops->configure(device, &device->config); + } + + /* release lock */ + rt_mutex_release(&(device->bus->lock)); + } + } + + return RT_EOK; +} + +rt_err_t rt_spi_send_then_send(struct rt_spi_device* device, const void *send_buf1, rt_size_t send_length1, + const void* send_buf2, rt_size_t send_length2) +{ + rt_err_t result; + struct rt_spi_message message; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + + result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (device->bus->owner != device) + { + /* not the same owner as current, re-configure SPI bus */ + result = device->bus->ops->configure(device, &device->config); + if (result == RT_EOK) + { + /* set SPI bus owner */ + device->bus->owner = device; + } + else + { + /* configure SPI bus failed */ + result = -RT_EIO; + goto __exit; + } + } + + /* send data1 */ + message.send_buf = send_buf1; + message.recv_buf = RT_NULL; + message.length = send_length1; + message.cs_take = 1; + message.cs_release = 0; + + result = device->bus->ops->xfer(device, &message); + if (result == 0) + { + result = -RT_EIO; + goto __exit; + } + + /* send data2 */ + message.send_buf = send_buf2; + message.recv_buf = RT_NULL; + message.length = send_length2; + message.cs_take = 0; + message.cs_release = 1; + + result = device->bus->ops->xfer(device, &message); + if (result == 0) + { + result = -RT_EIO; + goto __exit; + } + + result = RT_EOK; + } + else + { + return -RT_EIO; + } + +__exit: + rt_mutex_release(&(device->bus->lock)); + return result; +} + +rt_err_t rt_spi_send_then_recv(struct rt_spi_device* device, const void *send_buf, rt_size_t send_length, + void* recv_buf, rt_size_t recv_length) +{ + rt_err_t result; + struct rt_spi_message message; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + + result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (device->bus->owner != device) + { + /* not the same owner as current, re-configure SPI bus */ + result = device->bus->ops->configure(device, &device->config); + if (result == RT_EOK) + { + /* set SPI bus owner */ + device->bus->owner = device; + } + else + { + /* configure SPI bus failed */ + result = -RT_EIO; + goto __exit; + } + } + + /* send data */ + message.send_buf = send_buf; + message.recv_buf = RT_NULL; + message.length = send_length; + message.cs_take = 1; + message.cs_release = 0; + + result = device->bus->ops->xfer(device, &message); + if (result == 0) + { + result = -RT_EIO; + goto __exit; + } + + /* recv data */ + message.send_buf = RT_NULL; + message.recv_buf = recv_buf; + message.length = recv_length; + message.cs_take = 0; + message.cs_release = 1; + + result = device->bus->ops->xfer(device, &message); + if (result == 0) + { + result = -RT_EIO; + goto __exit; + } + + result = RT_EOK; + } + else + { + return -RT_EIO; + } + +__exit: + rt_mutex_release(&(device->bus->lock)); + return result; +} + +rt_size_t rt_spi_transfer(struct rt_spi_device* device, const void *send_buf, + void* recv_buf, rt_size_t length) +{ + rt_err_t result; + struct rt_spi_message message; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + + result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (device->bus->owner != device) + { + /* not the same owner as current, re-configure SPI bus */ + result = device->bus->ops->configure(device, &device->config); + if (result == RT_EOK) + { + /* set SPI bus owner */ + device->bus->owner = device; + } + else + { + /* configure SPI bus failed */ + rt_set_errno(-RT_EIO); + result = 0; + goto __exit; + } + } + + /* initial message */ + message.send_buf = send_buf; + message.recv_buf = recv_buf; + message.length = length; + message.cs_take = message.cs_release = 1; + + /* transfer message */ + result = device->bus->ops->xfer(device, &message); + if (result == 0) + { + rt_set_errno(-RT_EIO); + goto __exit; + } + } + else + { + rt_set_errno(-RT_EIO); + return 0; + } + +__exit: + rt_mutex_release(&(device->bus->lock)); + return result; +} diff --git a/components/drivers/spi/spi_dev.c b/components/drivers/spi/spi_dev.c new file mode 100644 index 000000000..afb17dafd --- /dev/null +++ b/components/drivers/spi/spi_dev.c @@ -0,0 +1,149 @@ +#include +#include + +/* SPI bus device interface, compatible with RT-Thread 0.3.x/1.0.x */ +static rt_err_t _spi_bus_device_init(rt_device_t dev) +{ + struct rt_spi_bus* bus; + + bus = (struct rt_spi_bus*) dev; + RT_ASSERT(bus != RT_NULL); + + return RT_EOK; +} + +static rt_size_t _spi_bus_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct rt_spi_bus* bus; + + bus = (struct rt_spi_bus*) dev; + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(bus->owner != RT_NULL); + + return rt_spi_transfer(bus->owner, RT_NULL, buffer, size); +} + +static rt_size_t _spi_bus_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct rt_spi_bus* bus; + + bus = (struct rt_spi_bus*) dev; + RT_ASSERT(bus != RT_NULL); + RT_ASSERT(bus->owner != RT_NULL); + + return rt_spi_transfer(bus->owner, buffer, RT_NULL, size); +} + +static rt_err_t _spi_bus_device_control(rt_device_t dev, rt_uint8_t cmd, void *args) +{ + struct rt_spi_bus* bus; + + bus = (struct rt_spi_bus*) dev; + RT_ASSERT(bus != RT_NULL); + + switch (cmd) + { + case 0: /* set device */ + break; + case 1: + break; + } + + return RT_EOK; +} + +rt_err_t rt_spi_bus_device_init(struct rt_spi_bus* bus, const char* name) +{ + struct rt_device *device; + RT_ASSERT(bus != RT_NULL); + + device = &bus->parent; + + /* set device type */ + device->type = RT_Device_Class_SPIBUS; + /* initialize device interface */ + device->init = _spi_bus_device_init; + device->open = RT_NULL; + device->close = RT_NULL; + device->read = _spi_bus_device_read; + device->write = _spi_bus_device_write; + device->control = _spi_bus_device_control; + + /* register to device manager */ + rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +} + +/* SPI Dev device interface, compatible with RT-Thread 0.3.x/1.0.x */ +static rt_err_t _spidev_device_init(rt_device_t dev) +{ + struct rt_spi_device* device; + + device = (struct rt_spi_device*) dev; + RT_ASSERT(device != RT_NULL); + + return RT_EOK; +} + +static rt_size_t _spidev_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct rt_spi_device* device; + + device = (struct rt_spi_device*) dev; + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + + return rt_spi_transfer(device, RT_NULL, buffer, size); +} + +static rt_size_t _spidev_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct rt_spi_device* device; + + device = (struct rt_spi_device*) dev; + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + + return rt_spi_transfer(device, buffer, RT_NULL, size); +} + +static rt_err_t _spidev_device_control(rt_device_t dev, rt_uint8_t cmd, void *args) +{ + struct rt_spi_device* device; + + device = (struct rt_spi_device*) dev; + RT_ASSERT(device != RT_NULL); + + switch (cmd) + { + case 0: /* set device */ + break; + case 1: + break; + } + + return RT_EOK; +} + +rt_err_t rt_spidev_device_init(struct rt_spi_device* dev, const char* name) +{ + struct rt_device* device; + RT_ASSERT(device != RT_NULL); + + device = &(dev->parent); + + /* set device type */ + device->type = RT_Device_Class_SPIDevice; + device->init = _spidev_device_init; + device->open = RT_NULL; + device->close = RT_NULL; + device->read = _spidev_device_read; + device->write = _spidev_device_write; + device->control = _spidev_device_control; + + /* register to device manager */ + rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +}