소스 검색

[DeviceDrivers][spi]:add qspi support

zylx 6 년 전
부모
커밋
257ce972d0
3개의 변경된 파일375개의 추가작업 그리고 0개의 파일을 삭제
  1. 108 0
      components/drivers/include/drivers/spi.h
  2. 265 0
      components/drivers/spi/qspi_core.c
  3. 2 0
      components/drivers/spi/spi_core.c

+ 108 - 0
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

+ 265 - 0
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;
+}

+ 2 - 0
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;
 }