DeviceTree
spi0: spi@f0020000 {
compatible = "snps,designware-spi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xf0020000 0x100>;
interrupts = <40 1>;
pinctrl-0 = <&spi2_default>;
pinctrl-1 = <&spi2_sleep>;
pinctrl-names = "default", "sleep";
fifo-depth = <32>; // 必须参数
clocks = <&sysclk>; // 和下面clock-frequency二选一,优先从&sysclk中获取clock-frequency
clock-frequency = <200000000>;
cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; // 用gpio代替cs,可选参数
status = "disabled";
slow@0 { // spi设备
compatible = "test-spi-loopback-slow";
reg = <0>;
spi-max-frequency = <500000>;
};
fast@0 { // @x 是指第几根片选
compatible = "test-spi-loopback-fast";
reg = <0>; // 0表示spi master controller,1表示spi slave controller
spi-max-frequency = <1000000>;
};
};
Consumer(应用层和其他driver如何调用spi接口)
方法1:
设备树API
参考zephyr/tests/drivers/spi/spi_loopback/src/spi.c
// 获取spi device node id
#define SPI_DEV DT_COMPAT_GET_ANY_STATUS_OKAY(test_spi_loopback) //这里注意是spi device的compatible,不是bus
// 定义spi controller属性
#define SPI_OP(frame_size) SPI_OP_MODE_MASTER | SPI_MODE_CPOL | MODE_LOOP | \
SPI_MODE_CPHA | SPI_WORD_SET(frame_size) | SPI_LINES_SINGLE
//传入spi device node id和config,通过SPI_DT_SPEC_GET宏从设备树获得spi_dt_spec结构体
struct spi_dt_spec {
const struct device *bus;
struct spi_config config;
};
#define SPI_DT_SPEC_GET(node_id, operation_, delay_) \
{ \
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
.config = SPI_CONFIG_DT(node_id, operation_, delay_) \
}
static struct spi_dt_spec spi = SPI_DT_SPEC_GET(SPI_DEV, SPI_OP(FRAME_SIZE), 0);
接着需要初始化spi_buf_set *tx_bufs
和spi_buf_set *rx_bufs
const struct spi_buf tx_bufs[] = {
{
.buf = buffer_tx,
.len = BUF_SIZE,
},
};
const struct spi_buf rx_bufs[] = {
{
.buf = buffer_rx,
.len = BUF_SIZE,
},
};
const struct spi_buf_set tx = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs)
};
const struct spi_buf_set rx = {
.buffers = rx_bufs,
.count = ARRAY_SIZE(rx_bufs)
};
最后可以调用spi_transceive_dt进行spi传输了。
static inline int spi_transceive_dt(const struct spi_dt_spec *spec,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
结束spi传输后需要调用spi_release_dt
来做收尾工作。
方法2:
直接调用spi_transceive
,这时候dev
, config
参数需要我们自己准备了。
不过这两个函数是`__syscall`,而上面devicetree的API不是,是否意味着user space只能调用下面这两个函数?
__syscall int spi_transceive(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs);
__syscall int spi_release(const struct device *dev,
const struct spi_config *config);
Provider(SSI driver)
SSI Master Flow:
// spi_dw_init
write_imr(info, DW_SPI_IMR_MASK); // SSI_IMR(0x2C) write 0
clear_bit_ssienr(info); // SSI_ENR(0x08) write 0
// spi_dw_configure
write_ctrlr0(info, ctrlr0); // SSI_CTRLR0(0x00) 写入上层指定的config,包括bit0-3,6,7,11。
write_baudr(info, SPI_DW_CLK_DIVIDER(info->clock_frequency, config->frequency)) // SSI_BAUDR(0x14) baud rate
write_ser(info, 1 << config->slave); // SSI_SER(0x10) write 0
// transceive
write_ctrlr1(info, reg_data); or write_ctrlr1(info, 0); // SSI_CTRLR1(0x04) rx mode write NDF
write_ctrlr0(info, reg_data); // SSI_CTRLR0(0x00) update TMOD bit8~9
spi_dw_update_txftlr(info, spi); // SSI_TXFTLR(0x18) Set Tx threshold is half of the fifo
write_rxftlr(info, reg_data); // SSI_RXFTLR(0x1C) Ser Rx threshold, default is (info->fifo_depth * 5) / 8
write_imr(info, reg_data); // SSI_IMR(0x2C) enable interrupt
spi_context_cs_control(&spi->ctx, true); // cs toggle low
set_bit_ssienr(info); // SSI_ENR(0x08) enable controller
// spi_dw_isr
// rx interrupt -> pull_data
read_rxflr(info); // SSI_RXFLR(0x24) number of valid data
read_dr(info); // SSI_DR(0x60) read data out
// tx interrupt -> push_data
read_txflr(info) // SSI_RXFLR(0x20) number of valid data
write_dr(info, data); // SSI_DR(0x60) write data in
clear_interrupts(info); // SSI_ICR(0x48) clear interrupt
test_bit_sr_busy(info); // SSI_SR(0x28) bit0 wait busy
write_imr(info, DW_SPI_IMR_MASK); // SSI_IMR(0x2C) disable interrupt
clear_bit_ssienr(info); // SSI_ENR(0x08) write 0
spi_context_cs_control(&spi->ctx, false); // cs toggle high
CONFIG_SPI_STATS开关
看看打开这个对debug有没有帮助