summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2018-11-22 20:25:04 +0800
committerGitHub <[email protected]>2018-11-22 20:25:04 +0800
commit94d85fa3b5cd49350d3d65f33a8b2f1801380f4c (patch)
treeae0c811b496872dd03e07211514068c0122f85b7
parent8ea5b770117cacea7f17b2b08305f360ed71bca7 (diff)
parent257ce972d0d54cb311274f04c270630e5c015288 (diff)
Merge pull request #1996 from qgyhd1234/qspi
[DeviceDrivers][spi]:add qspi support
-rw-r--r--components/drivers/include/drivers/spi.h108
-rw-r--r--components/drivers/spi/qspi_core.c265
-rw-r--r--components/drivers/spi/spi_core.c2
3 files changed, 375 insertions, 0 deletions
diff --git a/components/drivers/include/drivers/spi.h b/components/drivers/include/drivers/spi.h
index 84b589ace0..7394710581 100644
--- a/components/drivers/include/drivers/spi.h
+++ b/components/drivers/include/drivers/spi.h
@@ -45,6 +45,9 @@ extern "C"{
#define RT_SPI_MODE_MASK (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB)
+#define RT_SPI_BUS_MODE_SPI (1<<0)
+#define RT_SPI_BUS_MODE_QSPI (1<<1)
+
#define RT_SPI_CS_HIGH (1<<4) /* Chipselect active high */
#define RT_SPI_NO_CS (1<<5) /* No chipselect */
#define RT_SPI_3WIRE (1<<6) /* SI/SO pin shared */
@@ -80,6 +83,7 @@ struct rt_spi_ops;
struct rt_spi_bus
{
struct rt_device parent;
+ rt_uint8_t mode;
const struct rt_spi_ops *ops;
struct rt_mutex lock;
@@ -106,6 +110,55 @@ struct rt_spi_device
struct rt_spi_configuration config;
void *user_data;
};
+
+struct rt_qspi_message
+{
+ struct rt_spi_message parent;
+
+ /* instruction stage */
+ struct
+ {
+ rt_uint8_t content;
+ rt_uint8_t qspi_lines;
+ } instruction;
+
+ /* address and alternate_bytes stage */
+ struct
+ {
+ rt_uint32_t content;
+ rt_uint8_t size;
+ rt_uint8_t qspi_lines;
+ } address, alternate_bytes;
+
+ /* dummy_cycles stage */
+ rt_uint32_t dummy_cycles;
+
+ /* number of lines in qspi data stage, the other configuration items are in parent */
+ rt_uint8_t qspi_data_lines;
+};
+
+struct rt_qspi_configuration
+{
+ struct rt_spi_configuration parent;
+ /* The size of medium */
+ rt_uint32_t medium_size;
+ /* double data rate mode */
+ rt_uint8_t ddr_mode;
+ /* the number of lines connected to the hardware */
+ rt_uint8_t qspi_hw_lines;
+};
+
+struct rt_qspi_device
+{
+ struct rt_spi_device parent;
+
+ struct rt_qspi_configuration config;
+
+ void (*enter_qspi_mode)(struct rt_qspi_device *device);
+
+ void (*exit_qspi_mode)(struct rt_qspi_device *device);
+};
+
#define SPI_DEVICE(dev) ((struct rt_spi_device *)(dev))
/* register a SPI bus */
@@ -255,6 +308,61 @@ rt_inline void rt_spi_message_append(struct rt_spi_message *list,
message->next = RT_NULL;
}
+/**
+ * This function can set configuration on QSPI device.
+ *
+ * @param device the QSPI device attached to QSPI bus.
+ * @param cfg the configuration pointer.
+ *
+ * @return the actual length of transmitted.
+ */
+rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg);
+
+/**
+ * This function can register a SPI bus for QSPI mode.
+ *
+ * @param bus the SPI bus for QSPI mode.
+ * @param name The name of the spi bus.
+ * @param ops the SPI bus instance to be registered.
+ *
+ * @return the actual length of transmitted.
+ */
+rt_err_t rt_qspi_bus_register(struct rt_spi_bus *bus, const char *name, const struct rt_spi_ops *ops);
+
+/**
+ * This function transmits data to QSPI device.
+ *
+ * @param device the QSPI device attached to QSPI bus.
+ * @param message the message pointer.
+ *
+ * @return the actual length of transmitted.
+ */
+rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message);
+
+/**
+ * This function can send data then receive data from QSPI device
+ *
+ * @param device the QSPI device attached to QSPI bus.
+ * @param send_buf the buffer to be transmitted to QSPI device.
+ * @param send_length the number of data to be transmitted.
+ * @param recv_buf the buffer to be recivied from QSPI device.
+ * @param recv_length the data to be recivied.
+ *
+ * @return the status of transmit.
+ */
+rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length,void *recv_buf, rt_size_t recv_length);
+
+/**
+ * This function can send data to QSPI device
+ *
+ * @param device the QSPI device attached to QSPI bus.
+ * @param send_buf the buffer to be transmitted to QSPI device.
+ * @param send_length the number of data to be transmitted.
+ *
+ * @return the status of transmit.
+ */
+rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length);
+
#ifdef __cplusplus
}
#endif
diff --git a/components/drivers/spi/qspi_core.c b/components/drivers/spi/qspi_core.c
new file mode 100644
index 0000000000..7676764b59
--- /dev/null
+++ b/components/drivers/spi/qspi_core.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-16 zylx first version.
+ */
+
+#include <drivers/spi.h>
+
+rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg)
+{
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(cfg != RT_NULL);
+
+ struct rt_qspi_device *qspi_device = (struct rt_qspi_device *)device;
+ rt_err_t result = RT_EOK;
+
+ /* copy configuration items */
+ qspi_device->config.parent.mode = cfg->parent.mode;
+ qspi_device->config.parent.max_hz = cfg->parent.max_hz;
+ qspi_device->config.parent.data_width = cfg->parent.data_width;
+ qspi_device->config.parent.reserved = cfg->parent.reserved;
+ qspi_device->config.medium_size = cfg->medium_size;
+ qspi_device->config.ddr_mode = cfg->ddr_mode;
+ qspi_device->config.qspi_hw_lines = cfg->qspi_hw_lines;
+
+ result = rt_spi_configure(&device->parent, &cfg->parent);
+
+ return result;
+}
+
+rt_err_t rt_qspi_bus_register(struct rt_spi_bus *bus, const char *name, const struct rt_spi_ops *ops)
+{
+ rt_err_t result = RT_EOK;
+
+ result = rt_spi_bus_register(bus, name, ops);
+ if(result == RT_EOK)
+ {
+ /* set SPI bus to qspi modes */
+ bus->mode = RT_SPI_BUS_MODE_QSPI;
+ }
+
+ return result;
+}
+
+rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message)
+{
+ rt_err_t result;
+
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(message != RT_NULL);
+
+ result = rt_mutex_take(&(device->parent.bus->lock), RT_WAITING_FOREVER);
+ if (result != RT_EOK)
+ {
+ rt_set_errno(-RT_EBUSY);
+
+ return 0;
+ }
+
+ /* reset errno */
+ rt_set_errno(RT_EOK);
+
+ /* configure SPI bus */
+ if (device->parent.bus->owner != &device->parent)
+ {
+ /* not the same owner as current, re-configure SPI bus */
+ result = device->parent.bus->ops->configure(&device->parent, &device->parent.config);
+ if (result == RT_EOK)
+ {
+ /* set SPI bus owner */
+ device->parent.bus->owner = &device->parent;
+ }
+ else
+ {
+ /* configure SPI bus failed */
+ rt_set_errno(-RT_EIO);
+ goto __exit;
+ }
+ }
+
+ /* transmit each SPI message */
+
+ result = device->parent.bus->ops->xfer(&device->parent, &message->parent);
+ if (result == 0)
+ {
+ rt_set_errno(-RT_EIO);
+ }
+
+__exit:
+ /* release bus lock */
+ rt_mutex_release(&(device->parent.bus->lock));
+
+ return result;
+}
+
+rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length)
+{
+ RT_ASSERT(send_buf);
+ RT_ASSERT(recv_buf);
+ RT_ASSERT(send_length != 0);
+
+ struct rt_qspi_message message;
+ unsigned char *ptr = (unsigned char *)send_buf;
+ rt_size_t count = 0;
+ rt_err_t result = 0;
+
+ message.instruction.content = ptr[0];
+ message.instruction.qspi_lines = 1;
+ count++;
+
+ /* get address */
+ if (send_length > 1)
+ {
+ if (device->config.medium_size > 0x1000000 && send_length >= 5)
+ {
+ /* medium size greater than 16Mb, address size is 4 Byte */
+ message.address.content = (ptr[1] << 24) | (ptr[2] << 16) | (ptr[3] << 8) | (ptr[4]);
+ message.address.size = 32;
+ count += 4;
+ }
+ else if (send_length >= 4)
+ {
+ /* address size is 3 Byte */
+ message.address.content = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
+ message.address.size = 24;
+ count += 3;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+ message.address.qspi_lines = 1;
+ }
+ else
+ {
+ /* no address stage */
+ message.address.content = 0 ;
+ message.address.qspi_lines = 0;
+ message.address.size = 0;
+ }
+
+ message.alternate_bytes.content = 0;
+ message.alternate_bytes.size = 0;
+ message.alternate_bytes.qspi_lines = 0;
+
+ /* set dummy cycles */
+ if (count != send_length)
+ {
+ message.dummy_cycles = (send_length - count) * 8;
+
+ }
+ else
+ {
+ message.dummy_cycles = 0;
+ }
+
+ /* set recv buf and recv size */
+ message.parent.recv_buf = recv_buf;
+ message.parent.send_buf = RT_NULL;
+ message.parent.length = recv_length;
+ message.parent.cs_take = 1;
+ message.parent.cs_release = 1;
+
+ message.qspi_data_lines = 1;
+
+ result = rt_qspi_transfer_message(device, &message);
+ if (result == 0)
+ {
+ result = -RT_EIO;
+ }
+ else
+ {
+ result = recv_length;
+ }
+
+ return result;
+}
+
+rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length)
+{
+ RT_ASSERT(send_buf);
+ RT_ASSERT(length != 0);
+
+ struct rt_qspi_message message;
+ char *ptr = (char *)send_buf;
+ rt_size_t count = 0;
+ rt_err_t result = 0;
+
+ message.instruction.content = ptr[0];
+ message.instruction.qspi_lines = 1;
+ count++;
+
+ /* get address */
+ if (length > 1)
+ {
+ if (device->config.medium_size > 0x1000000 && length >= 5)
+ {
+ /* medium size greater than 16Mb, address size is 4 Byte */
+ message.address.content = (ptr[1] << 24) | (ptr[2] << 16) | (ptr[3] << 8) | (ptr[4]);
+ message.address.size = 32;
+ message.address.qspi_lines = 1;
+ count += 4;
+ }
+ else if (length >= 4)
+ {
+ /* address size is 3 Byte */
+ message.address.content = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
+ message.address.size = 24;
+ message.address.qspi_lines = 1;
+ count += 3;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+
+ }
+ else
+ {
+ /* no address stage */
+ message.address.content = 0 ;
+ message.address.qspi_lines = 0;
+ message.address.size = 0;
+ }
+
+ message.alternate_bytes.content = 0;
+ message.alternate_bytes.size = 0;
+ message.alternate_bytes.qspi_lines = 0;
+
+ message.dummy_cycles = 0;
+
+ /* determine if there is data to send */
+ if (length - count > 0)
+ {
+ message.qspi_data_lines = 1;
+ }
+ else
+ {
+ message.qspi_data_lines = 0;
+
+ }
+
+ /* set send buf and send size */
+ message.parent.send_buf = ptr + count;
+ message.parent.recv_buf = RT_NULL;
+ message.parent.length = length - count;
+ message.parent.cs_take = 1;
+ message.parent.cs_release = 1;
+
+ result = rt_qspi_transfer_message(device, &message);
+ if (result == 0)
+ {
+ result = -RT_EIO;
+ }
+ else
+ {
+ result = length;
+ }
+
+ return result;
+}
diff --git a/components/drivers/spi/spi_core.c b/components/drivers/spi/spi_core.c
index 9e404d7c94..64b0d2e496 100644
--- a/components/drivers/spi/spi_core.c
+++ b/components/drivers/spi/spi_core.c
@@ -34,6 +34,8 @@ rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus,
bus->ops = ops;
/* initialize owner */
bus->owner = RT_NULL;
+ /* set bus mode */
+ bus->mode = RT_SPI_BUS_MODE_SPI;
return RT_EOK;
}