SPI总线驱动、设备驱动
SPI驱动分析
zynq SPI控制器理解记录
如下:spi一般都由一下两个不通的模式组合成四个模式:
自动/手到发送数据:
自动:当TxFIFO有数据就进行发送;无数据则停止发送。
手动:通过使能发送位进行数据的发送。
自动/手动控制CS使能信号:
自动:当TXFIFO有数据的时候就自动使能,Txfifo数据传输完成之后就不使能
手动:通过使能位来软件控制片选信号的使能
SPI总线
一个SOC有多个spi控制器,这些控制器都是连接在SPI总线上,linux采用SPI总线来管理这些设备。
其中driver/spi/spi.c中实现了对spi总线的注册,同时为上层设备提供了统一的接口,以下是spi.c中初始化代码
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
/* board_info is normally registered in arch_initcall(),
* but even essential drivers wait till later
*
* REVISIT only boardinfo really needs static linking. the rest (device and
* driver registration) _could_ be dynamically linked (modular) ... costs
* include needing to have boardinfo data structures be much more public.
*/
postcore_initcall(spi_init);
其中postcore_initcall(*)是一个宏,内核起来的时候会执行宏里面定义的函数。
status = bus_register(&spi_bus_type);实现对SPI总线的注册
SPI Master控制器驱动
SPI Master控制器是SOC内部的控制器,一般一个SOC有多个控制器,使用同一个控制器驱动,对应的代码在drivers/spi/spi-xxx.c。每一个文件对应着不通的spi控制器驱动,本文以spi-candance.c为例
/**
* cdns_spi_probe - Probe method for the SPI driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success and error value on error
*/
static int cdns_spi_probe(struct platform_device *pdev)
{
int ret = 0, irq;
struct spi_master *master;
struct cdns_spi *xspi;
struct resource *res;
u32 num_cs;
//pr_info("cdns_spi_probe....\r\n");
//pr_info("sizeof(*xspi)=%d\n", sizeof(*xspi));
master = spi_alloc_master(&pdev->dev, sizeof(*xspi));
if (master == NULL)
return -ENOMEM;
xspi = spi_master_get_devdata(master);
master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, master);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//pr_info("addr:0x%x\n", res->start);
xspi->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(xspi->regs)) {
ret = PTR_ERR(xspi->regs);
goto remove_master;
}
xspi->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(xspi->pclk)) {
dev_err(&pdev->dev, "pclk clock not found.\n");
ret = PTR_ERR(xspi->pclk);
goto remove_master;
}
xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
if (IS_ERR(xspi->ref_clk)) {
dev_err(&pdev->dev, "ref_clk clock not found.\n");
ret = PTR_ERR(xspi->ref_clk);
goto remove_master;
}
ret = clk_prepare_enable(xspi->pclk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable APB clock.\n");
goto remove_master;
}
ret = clk_prepare_enable(xspi->ref_clk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable device clock.\n");
goto clk_dis_apb;
}
/* SPI controller initializations */
cdns_spi_init_hw(xspi);
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
ret = -ENXIO;
dev_err(&pdev->dev, "irq number is invalid\n");
goto remove_master;
}
ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq,
0, pdev->name, master);
if (ret != 0) {
ret = -ENXIO;
dev_err(&pdev->dev, "request_irq failed\n");
goto remove_master;
}
ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
//pr_info("num_cs = %d\n", num_cs);
if (ret < 0)
master->num_chipselect = CDNS_SPI_DEFAULT_NUM_CS;
else
master->num_chipselect = num_cs;
ret = of_property_read_u32(pdev->dev.of_node, "is-decoded-cs",
&xspi->is_decoded_cs);
//pr_info("is-decoded-cs = %d\n", xspi->is_decoded_cs);
if (ret < 0)
xspi->is_decoded_cs = 0;
master->prepare_transfer_hardware = cdns_prepare_transfer_hardware;
master->prepare_message = cdns_prepare_message;
master->transfer_one = cdns_transfer_one;
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
master->set_cs = cdns_spi_chipselect;
master->mode_bits = SPI_CPOL | SPI_CPHA;
/* Set to default valid value */
master->max_speed_hz = clk_get_rate(xspi->ref_clk) / 4;
xspi->speed_hz = master->max_speed_hz;
master->bits_per_word_mask = SPI_BPW_MASK(8);
ret = spi_register_master(master);
if (ret) {
dev_err(&pdev->dev, "spi_register_master failed\n");
goto clk_dis_all;
}
return ret;
clk_dis_all:
clk_disable_unprepare(xspi->ref_clk);
clk_dis_apb:
clk_disable_unprepare(xspi->pclk);
remove_master:
spi_master_put(master);
return ret;
}
驱动就是linux驱动框架的三部曲
①,创建一个spi_master结构体
②,硬件初始化
③,结构体设置(相关驱动函数的编写,根据设备数进行相应的测试)
④,注册结构体
SPI Device驱动
spi针对应用功能程序的驱动接口实现在drivers/spi/spidev.c里面实现,跟普通的字符设备驱动一样,实现设备的生成注册;设备驱动函数open、write、read、open函数的实现;这些函数直接调用通用层spi.c里的函数,所以本源码文件也是通用的,不需要针对不同的控制器进行修改。
其他外设使用SPI通信
其他外设在设备树编写的时候作为spi控制器下的一个子设备,在对应的驱动程序的时候调用spi.c提供的接口即可使用对应spi控制器进行控制。
static int ds1305_probe(struct spi_device *spi)
{
struct ds1305 *ds1305;
int status;
u8 addr, value;
struct ds1305_platform_data *pdata = dev_get_platdata(&spi->dev);
bool write_ctrl = false;
/* Sanity check board setup data. This may be hooked up
* in 3wire mode, but we don't care. Note that unless
* there's an inverter in place, this needs SPI_CS_HIGH!
*/
if ((spi->bits_per_word && spi->bits_per_word != 8)
|| (spi->max_speed_hz > 2000000)
|| !(spi->mode & SPI_CPHA))
return -EINVAL;
/* set up driver data */
ds1305 = devm_kzalloc(&spi->dev, sizeof(*ds1305), GFP_KERNEL);
if (!ds1305)
return -ENOMEM;
ds1305->spi = spi;
spi_set_drvdata(spi, ds1305);
/* read and cache control registers */
addr = DS1305_CONTROL;
status = spi_write_then_read(spi, &addr, sizeof(addr),
ds1305->ctrl, sizeof(ds1305->ctrl));
if (status < 0) {
dev_dbg(&spi->dev, "can't %s, %d\n",
"read", status);
return status;
}
dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "read", ds1305->ctrl);
/* Sanity check register values ... partially compensating for the
* fact that SPI has no device handshake. A pullup on MISO would
* make these tests fail; but not all systems will have one. If
* some register is neither 0x00 nor 0xff, a chip is likely there.
*/
if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) {
dev_dbg(&spi->dev, "RTC chip is not present\n");
return -ENODEV;
}
if (ds1305->ctrl[2] == 0)
dev_dbg(&spi->dev, "chip may not be present\n");
/* enable writes if needed ... if we were paranoid it would
* make sense to enable them only when absolutely necessary.
*/
if (ds1305->ctrl[0] & DS1305_WP) {
u8 buf[2];
ds1305->ctrl[0] &= ~DS1305_WP;
buf[0] = DS1305_WRITE | DS1305_CONTROL;
buf[1] = ds1305->ctrl[0];
status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
dev_dbg(&spi->dev, "clear WP --> %d\n", status);
if (status < 0)
return status;
}
/* on DS1305, maybe start oscillator; like most low power
* oscillators, it may take a second to stabilize
*/
if (ds1305->ctrl[0] & DS1305_nEOSC) {
ds1305->ctrl[0] &= ~DS1305_nEOSC;
write_ctrl = true;
dev_warn(&spi->dev, "SET TIME!\n");
}
/* ack any pending IRQs */
if (ds1305->ctrl[1]) {
ds1305->ctrl[1] = 0;
write_ctrl = true;
}
/* this may need one-time (re)init */
if (pdata) {
/* maybe enable trickle charge */
if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) {
ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC
| pdata->trickle;
write_ctrl = true;
}
/* on DS1306, configure 1 Hz signal */
if (pdata->is_ds1306) {
if (pdata->en_1hz) {
if (!(ds1305->ctrl[0] & DS1306_1HZ)) {
ds1305->ctrl[0] |= DS1306_1HZ;
write_ctrl = true;
}
} else {
if (ds1305->ctrl[0] & DS1306_1HZ) {
ds1305->ctrl[0] &= ~DS1306_1HZ;
write_ctrl = true;
}
}
}
}
if (write_ctrl) {
u8 buf[4];
buf[0] = DS1305_WRITE | DS1305_CONTROL;
buf[1] = ds1305->ctrl[0];
buf[2] = ds1305->ctrl[1];
buf[3] = ds1305->ctrl[2];
status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
if (status < 0) {
dev_dbg(&spi->dev, "can't %s, %d\n",
"write", status);
return status;
}
dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "write", ds1305->ctrl);
}
/* see if non-Linux software set up AM/PM mode */
addr = DS1305_HOUR;
status = spi_write_then_read(spi, &addr, sizeof(addr),
&value, sizeof(value));
if (status < 0) {
dev_dbg(&spi->dev, "read HOUR --> %d\n", status);
return status;
}
ds1305->hr12 = (DS1305_HR_12 & value) != 0;
if (ds1305->hr12)
dev_dbg(&spi->dev, "AM/PM\n");
/* register RTC ... from here on, ds1305->ctrl needs locking */
ds1305->rtc = devm_rtc_device_register(&spi->dev, "ds1305",
&ds1305_ops, THIS_MODULE);
if (IS_ERR(ds1305->rtc)) {
status = PTR_ERR(ds1305->rtc);
dev_dbg(&spi->dev, "register rtc --> %d\n", status);
return status;
}
/* Maybe set up alarm IRQ; be ready to handle it triggering right
* away. NOTE that we don't share this. The signal is active low,
* and we can't ack it before a SPI message delay. We temporarily
* disable the IRQ until it's acked, which lets us work with more
* IRQ trigger modes (not all IRQ controllers can do falling edge).
*/
if (spi->irq) {
INIT_WORK(&ds1305->work, ds1305_work);
status = devm_request_irq(&spi->dev, spi->irq, ds1305_irq,
0, dev_name(&ds1305->rtc->dev), ds1305);
if (status < 0) {
dev_err(&spi->dev, "request_irq %d --> %d\n",
spi->irq, status);
} else {
device_set_wakeup_capable(&spi->dev, 1);
}
}
/* export NVRAM */
status = sysfs_create_bin_file(&spi->dev.kobj, &nvram);
if (status < 0) {
dev_err(&spi->dev, "register nvram --> %d\n", status);
}
return 0;
}
spi_set_drvdata(spi, ds1305); 把spi相关的驱动保存到此设备当中,之后的此设备驱动程序就可以利用spi来进行数据通信
直接采用spi_write_then_read()函数来实现spi的读写。
源码记录
问: spi fifo只有一定的大小,应用程序一次传递了超过fifo的数据,怎么处理
答: 每次只把fifo写满,然后开启发送fifo阈值中断,当fifo中的数据数量少于一定的值时中断产生,当发起的传输还有数据没有发送时则继续发送,直到所有的数据发送完成。
问: spi通用传输的片选信号在那里控制
答: spi.c里面在一次传输开始之前会先进行片选,而片选的具体操作函数则在spi-xxx.c里面实现
问题记录
一次传递一定数量的时候出现了超时的现象
spi.c里在开始一次传输之后设定一个等待值等待数据传输完成,这里的
ms = xfer->len * 8 * 1000 / xfer->speed_hz;计算在一定值得时候是零,就会出现在这个为零的值得数量的时候出现超时的情况,可以吧 tolerance的值加大,从而使其不出现超时的情况
if (ret > 0) {
ret = 0;
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
ms += 100; /* some tolerance */
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms));
}
完整函数如下:
static int spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct spi_transfer *xfer;
bool keep_cs = false;
int ret = 0;
int ms = 1;
//printk("%s,%d\r\n",__func__,__LINE__);
spi_set_cs(msg->spi, true);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);
reinit_completion(&master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer);
if (ret < 0) {
dev_err(&msg->spi->dev,
"SPI transfer failed: %d\n", ret);
goto out;
}
if (ret > 0) {
ret = 0;
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
ms += 100; /* some tolerance */
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms));
}
if (ms == 0) {
dev_err(&msg->spi->dev, "SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
}
trace_spi_transfer_stop(msg, xfer);
if (msg->status != -EINPROGRESS)
goto out;
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
keep_cs = true;
} else {
spi_set_cs(msg->spi, false);
udelay(10);
spi_set_cs(msg->spi, true);
}
}
msg->actual_length += xfer->len;
}
out:
if (ret != 0 || !keep_cs)
spi_set_cs(msg->spi, false);
if (msg->status == -EINPROGRESS)
msg->status = ret;
spi_finalize_current_message(master);
return ret;
}