240 lines
6.6 KiB
C
240 lines
6.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2021 hpm
|
||
|
*
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*
|
||
|
*/
|
||
|
#include <rtthread.h>
|
||
|
|
||
|
#ifdef BSP_USING_SPI
|
||
|
#include <rtdevice.h>
|
||
|
#include "board.h"
|
||
|
#include "drv_spi.h"
|
||
|
#include "hpm_spi_drv.h"
|
||
|
#include "hpm_sysctl_drv.h"
|
||
|
|
||
|
struct hpm_spi
|
||
|
{
|
||
|
uint32_t instance;
|
||
|
char *bus_name;
|
||
|
SPI_Type *spi_base;
|
||
|
spi_control_config_t control_config;
|
||
|
struct rt_spi_bus spi_bus;
|
||
|
rt_sem_t xfer_sem;
|
||
|
/* TODO: add DMA support later */
|
||
|
};
|
||
|
|
||
|
static rt_err_t hpm_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg);
|
||
|
static rt_uint32_t hpm_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *msg);
|
||
|
|
||
|
static struct hpm_spi hpm_spis[] =
|
||
|
{
|
||
|
#if defined(BSP_USING_SPI0)
|
||
|
{
|
||
|
.bus_name = "spi0",
|
||
|
.spi_base = HPM_SPI0,
|
||
|
},
|
||
|
#endif
|
||
|
#if defined(BSP_USING_SPI1)
|
||
|
{
|
||
|
.bus_name = "spi1",
|
||
|
.spi_base = HPM_SPI1,
|
||
|
},
|
||
|
#endif
|
||
|
#if defined(BSP_USING_SPI2)
|
||
|
{
|
||
|
.bus_name = "spi2",
|
||
|
.spi_base = HPM_SPI2,
|
||
|
},
|
||
|
#endif
|
||
|
#if defined(BSP_USING_SPI3)
|
||
|
{
|
||
|
.bus_name = "spi3",
|
||
|
.spi_base = HPM_SPI3,
|
||
|
},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static struct rt_spi_ops hpm_spi_ops =
|
||
|
{
|
||
|
.configure = hpm_spi_configure,
|
||
|
.xfer = hpm_spi_xfer,
|
||
|
};
|
||
|
|
||
|
static rt_err_t hpm_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
|
||
|
{
|
||
|
spi_timing_config_t timing_config = { 0 };
|
||
|
spi_format_config_t format_config = { 0 };
|
||
|
|
||
|
struct hpm_spi *spi = RT_NULL;
|
||
|
|
||
|
spi = (struct hpm_spi *) (device->bus->parent.user_data);
|
||
|
RT_ASSERT(spi != RT_NULL);
|
||
|
|
||
|
if (cfg->data_width != 8 && cfg->data_width != 16 && cfg->data_width != 32)
|
||
|
{
|
||
|
return RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
spi_master_get_default_timing_config(&timing_config);
|
||
|
spi_master_get_default_format_config(&format_config);
|
||
|
|
||
|
init_spi_pins(spi->spi_base);
|
||
|
|
||
|
timing_config.master_config.clk_src_freq_in_hz = board_init_spi_clock(spi->spi_base);
|
||
|
|
||
|
format_config.common_config.data_len_in_bits = cfg->data_width;
|
||
|
format_config.common_config.cpha = cfg->mode & RT_SPI_CPHA ? 1 : 0;
|
||
|
format_config.common_config.cpol = cfg->mode & RT_SPI_CPOL ? 1 : 0;
|
||
|
format_config.common_config.lsb = cfg->mode & RT_SPI_MSB ? false : true;
|
||
|
format_config.common_config.mosi_bidir = cfg->mode & RT_SPI_3WIRE ? true : false;
|
||
|
spi_format_init(spi->spi_base, &format_config);
|
||
|
|
||
|
if (cfg->max_hz > timing_config.master_config.clk_src_freq_in_hz)
|
||
|
{
|
||
|
cfg->max_hz = timing_config.master_config.clk_src_freq_in_hz;
|
||
|
}
|
||
|
timing_config.master_config.sclk_freq_in_hz = cfg->max_hz;
|
||
|
|
||
|
spi_master_timing_init(spi->spi_base, &timing_config);
|
||
|
|
||
|
|
||
|
spi_master_get_default_control_config(&spi->control_config);
|
||
|
spi->control_config.master_config.addr_enable = false;
|
||
|
spi->control_config.master_config.cmd_enable = false;
|
||
|
spi->control_config.master_config.token_enable = false;
|
||
|
spi->control_config.common_config.trans_mode = spi_trans_write_read_together;
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
static rt_uint32_t hpm_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *msg)
|
||
|
{
|
||
|
RT_ASSERT(device != RT_NULL);
|
||
|
RT_ASSERT(msg != RT_NULL);
|
||
|
RT_ASSERT(device->bus != RT_NULL);
|
||
|
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
|
||
|
|
||
|
cs_ctrl_callback_t cs_pin_control = (cs_ctrl_callback_t) device->parent.user_data;
|
||
|
|
||
|
struct hpm_spi *spi = (struct hpm_spi *) (device->bus->parent.user_data);
|
||
|
|
||
|
hpm_stat_t spi_stat = status_success;
|
||
|
|
||
|
if ((cs_pin_control != NULL) && msg->cs_take)
|
||
|
{
|
||
|
cs_pin_control(SPI_CS_TAKE);
|
||
|
}
|
||
|
|
||
|
uint32_t remaining_size = msg->length;
|
||
|
uint32_t transfer_len;
|
||
|
uint8_t *tx_buf = (uint8_t*) msg->send_buf;
|
||
|
uint8_t *rx_buf = (uint8_t*) msg->recv_buf;
|
||
|
while (remaining_size > 0)
|
||
|
{
|
||
|
transfer_len = MIN(512, remaining_size);
|
||
|
if (msg->send_buf != NULL && msg->recv_buf != NULL)
|
||
|
{
|
||
|
spi->control_config.common_config.trans_mode = spi_trans_write_read_together;
|
||
|
spi_stat = spi_transfer(spi->spi_base,
|
||
|
&spi->control_config,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
tx_buf, transfer_len,
|
||
|
rx_buf, transfer_len);
|
||
|
}
|
||
|
else if (msg->send_buf != NULL)
|
||
|
{
|
||
|
spi->control_config.common_config.trans_mode = spi_trans_write_only;
|
||
|
spi_stat = spi_transfer(spi->spi_base, &spi->control_config,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
(uint8_t*) tx_buf, transfer_len,
|
||
|
NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
spi->control_config.common_config.trans_mode = spi_trans_read_only;
|
||
|
spi_stat = spi_transfer(spi->spi_base, &spi->control_config,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL, 0,
|
||
|
rx_buf, transfer_len);
|
||
|
}
|
||
|
|
||
|
if (spi_stat != status_success)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (tx_buf != NULL)
|
||
|
{
|
||
|
tx_buf += transfer_len;
|
||
|
}
|
||
|
if (rx_buf != NULL)
|
||
|
{
|
||
|
rx_buf += transfer_len;
|
||
|
}
|
||
|
remaining_size -= transfer_len;
|
||
|
}
|
||
|
if (spi_stat != status_success)
|
||
|
{
|
||
|
msg->length = 0;
|
||
|
}
|
||
|
|
||
|
if ((cs_pin_control != NULL) && msg->cs_release)
|
||
|
{
|
||
|
cs_pin_control(SPI_CS_RELEASE);
|
||
|
}
|
||
|
|
||
|
return msg->length;
|
||
|
}
|
||
|
|
||
|
|
||
|
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, cs_ctrl_callback_t callback)
|
||
|
{
|
||
|
RT_ASSERT(bus_name != RT_NULL);
|
||
|
RT_ASSERT(device_name != RT_NULL);
|
||
|
|
||
|
rt_err_t result;
|
||
|
struct rt_spi_device *spi_device;
|
||
|
|
||
|
/* attach the device to spi bus*/
|
||
|
spi_device = (struct rt_spi_device *) rt_malloc(sizeof(struct rt_spi_device));
|
||
|
RT_ASSERT(spi_device != RT_NULL);
|
||
|
|
||
|
result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void*)callback);
|
||
|
RT_ASSERT(result == RT_EOK);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
int rt_hw_spi_init(void)
|
||
|
{
|
||
|
|
||
|
rt_err_t ret = RT_EOK;
|
||
|
|
||
|
for (uint32_t i = 0; i < sizeof(hpm_spis) / sizeof(hpm_spis[0]); i++)
|
||
|
{
|
||
|
hpm_spis[i].spi_bus.parent.user_data = &hpm_spis[i];
|
||
|
|
||
|
ret = rt_spi_bus_register(&hpm_spis[i].spi_bus, hpm_spis[i].bus_name, &hpm_spi_ops);
|
||
|
if (ret != RT_EOK)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
char sem_name[RT_NAME_MAX];
|
||
|
rt_sprintf(sem_name, "%s_s", hpm_spis[i].bus_name);
|
||
|
hpm_spis[i].xfer_sem = rt_sem_create(sem_name, 0, RT_IPC_FLAG_PRIO);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
INIT_BOARD_EXPORT(rt_hw_spi_init);
|
||
|
|
||
|
#endif /*BSP_USING_SPI*/
|
||
|
|