前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux内核驱动模型详解_arduino驱动安装

linux内核驱动模型详解_arduino驱动安装

作者头像
全栈程序员站长
发布2022-11-02 16:40:48
11.1K0
发布2022-11-02 16:40:48
举报

转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter

Linux SPI驱动分为核心层,控制器驱动层和设备驱动层。核心层是Linux的SPI核心部分,提供了核心数据结构的定义,总线、设备和驱动的注册、注销管理等,提供与上层的统一接口。linux将I2C、SPI、USB等总线驱动隔离成控制器驱动和设备驱动,使两者相对独立。 本文以qcom的spi控制器为例,对spi控制器驱动进行解析。kernel代码版本是3.18。

1 控制器设备注册

控制器的设备注册在kernel启动阶段的dt解析过程中完成,注册控制器设备为platform_device。dts中描述如下:

代码语言:javascript
复制
spi_6: spi@757a000 { 
 /* BLSP1 QUP6 */
compatible = "qcom,spi-qup-v2";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "spi_physical", "spi_bam_physical";
reg = <0x0757a000 0x600>,
<0x07544000 0x2b000>;
interrupt-names = "spi_irq", "spi_bam_irq";
interrupts = <0 100 0>, <0 238 0>;
spi-max-frequency = <19200000>;
qcom,infinite-mode = <0>;
qcom,rt-priority;
qcom,use-bam;
qcom,ver-reg-exists;
qcom,bam-consumer-pipe-index = <22>;
qcom,bam-producer-pipe-index = <23>;
qcom,master-id = <86>;
qcom,use-pinctrl;
pinctrl-names = "spi_default", "spi_sleep";
pinctrl-0 = <&spi_6_active>;
pinctrl-1 = <&spi_6_sleep>;
clock-names = "iface_clk", "core_clk";
clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
<&clock_gcc clk_gcc_blsp1_qup6_spi_apps_clk>;
};

2 控制器驱动注册

控制器设计者一般都会自定义一个数据结构用于描述控制器基本信息,不同设计厂商的数据结构成员不同,但肯定与自身控制器的使用密切相关的内容,比如内存基地址,clock,中断号,端口fifo大小等。linux驱动与设备是一对多的关系,在spi_master设备注册时,控制器的结构体信息会提供给spi_master作为私有数据。

代码语言:javascript
复制
struct spi_qup { 

void __iomem		*base;   //controller基址,操作controller寄存器时使用
struct device		*dev;
struct clk		*cclk;	/* core clock */  
struct clk		*iclk;	/* interface clock */
int			irq;
spinlock_t		lock;
int			in_fifo_sz;  
int			out_fifo_sz;
int			in_blk_sz;
int			out_blk_sz;
struct spi_transfer	*xfer;
struct completion	done;
int			error;
int			w_size;	/* bytes per SPI word */
int			tx_bytes;
int			rx_bytes;
int			qup_v1;
};

除了platform_device的控制器设备注册外,spi控制器还需要注册spi_bus_type的spi_master设备作为spi master,此数据接口是控制器驱动的核心数据结构。

代码语言:javascript
复制
struct spi_master {
struct device	dev;
struct list_head list;
/* other than negative (== assign one dynamically), bus_num is fully
* board-specific.  usually that simplifies to being SOC-specific.
* example:  one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2.  software
* would normally use bus_num=2 for that controller.
*/
s16			bus_num;
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16			num_chipselect;
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16			dma_alignment;
/* spi_device.mode flags understood by this controller driver */
u16			mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* limits on transfer speed */
u32			min_speed_hz;
u32			max_speed_hz;
/* other constraints relevant to this driver */
u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* can't do full duplex */
#define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
#define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)		/* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)		/* requires tx */
/* lock and mutex for SPI bus locking */
spinlock_t		bus_lock_spinlock;
struct mutex		bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool			bus_lock_flag;
/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT:  this may be called when transfers to another
* device are active.  DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int			(*setup)(struct spi_device *spi);
/* bidirectional bulk transfers
*
* + The transfer() method may not sleep; its main role is
*   just to add the message to the queue.
* + For now there's no remove-from-queue operation, or
*   any other request management
* + To a given spi_device, message queueing is pure fifo
*
* + The master's main job is to process its message queue,
*   selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
*   arbitration algorithm is unspecified (round robin, fifo,
*   priority, reservations, preemption, etc)
*
* + Chipselect stays active during the entire message
*   (unless modified by spi_transfer.cs_change != 0).
* + The message transfers use clock and SPI mode parameters
*   previously established by setup() for this device
*/
int			(*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* called on release() to free memory provided by spi_master */
void			(*cleanup)(struct spi_device *spi);
/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called.  The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool			(*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool				queued;
struct kthread_worker		kworker; 
struct task_struct		*kworker_task; //worker对应的task,task一直遍历执行添加到worker中list等待的work
struct kthread_work		pump_messages; //待添加到worker的work
spinlock_t			queue_lock;
struct list_head		queue;
struct spi_message		*cur_msg;
bool				busy;
bool				running;
bool				rt;
bool				auto_runtime_pm;
bool                            cur_msg_prepared;
bool				cur_msg_mapped;
struct completion               xfer_completion;
size_t				max_dma_len;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
/* gpio chip select */
int			*cs_gpios;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan		*dma_tx;
struct dma_chan		*dma_rx;
/* dummy data for full duplex devices */
void			*dummy_rx;
void			*dummy_tx;
};

spi控制器是platform_device,注册的驱动是plaform_driver。它的probe依赖于dts中设备的compatible属性与驱动中of_device_id的比对结果,一致的情况下,probe会被加载执行。 spi_qup.c

代码语言:javascript
复制
static const struct of_device_id spi_qup_dt_match[] = {
{ .compatible = "qcom,spi-qup-v1.1.1", },
{ .compatible = "qcom,spi-qup-v2.1.1", },
{ .compatible = "qcom,spi-qup-v2.2.1", },
{ }
};
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
static struct platform_driver spi_qup_driver = {
.driver = {
.name		= "spi_qup",
.owner		= THIS_MODULE,
.pm		= &spi_qup_dev_pm_ops,
.of_match_table = spi_qup_dt_match,
},
.probe = spi_qup_probe,
.remove = spi_qup_remove,
};
module_platform_driver(spi_qup_driver);

probe函数的工作大体包括,

  1. 解析dts,对控制器结构体spi_qup填充,设置控制器工作状态,注册中断等与厂商控制器自身相关
  2. spi_master结构体填充,注册spi_master设备 spi_qup.c
代码语言:javascript
复制
static int spi_qup_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct clk *iclk, *cclk;
struct spi_qup *controller;
struct resource *res;
struct device *dev;
void __iomem *base;
u32 max_freq, iomode, num_cs;
int ret, irq, size;
dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
cclk = devm_clk_get(dev, "core");
if (IS_ERR(cclk))
return PTR_ERR(cclk);
iclk = devm_clk_get(dev, "iface");
if (IS_ERR(iclk))
return PTR_ERR(iclk);
/* This is optional parameter */
if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
max_freq = SPI_MAX_RATE;
if (!max_freq || max_freq > SPI_MAX_RATE) {
dev_err(dev, "invalid clock frequency %d\n", max_freq);
return -ENXIO;
}
ret = clk_prepare_enable(cclk);
if (ret) {
dev_err(dev, "cannot enable core clock\n");
return ret;
}
ret = clk_prepare_enable(iclk);
if (ret) {
clk_disable_unprepare(cclk);
dev_err(dev, "cannot enable iface clock\n");
return ret;
}
master = spi_alloc_master(dev, sizeof(struct spi_qup));
if (!master) {
clk_disable_unprepare(cclk);
clk_disable_unprepare(iclk);
dev_err(dev, "cannot allocate master\n");
return -ENOMEM;
}
/* use num-cs unless not present or out of range */
if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) ||
num_cs > SPI_NUM_CHIPSELECTS)
master->num_chipselect = SPI_NUM_CHIPSELECTS;
else
master->num_chipselect = num_cs;
master->bus_num = pdev->id;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->max_speed_hz = max_freq;
master->transfer_one = spi_qup_transfer_one;
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
platform_set_drvdata(pdev, master);  //pdev->dev->dradata = master
controller = spi_master_get_devdata(master);   //master->dev->drvdata = controller
controller->dev = dev;
controller->base = base;
controller->iclk = iclk;
controller->cclk = cclk;
controller->irq = irq;
/* set v1 flag if device is version 1 */
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
controller->qup_v1 = 1;
spin_lock_init(&controller->lock);
init_completion(&controller->done);
iomode = readl_relaxed(base + QUP_IO_M_MODES);
size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
if (size)
controller->out_blk_sz = size * 16;
else
controller->out_blk_sz = 4;
size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
if (size)
controller->in_blk_sz = size * 16;
else
controller->in_blk_sz = 4;
size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
controller->in_blk_sz, controller->in_fifo_sz,
controller->out_blk_sz, controller->out_fifo_sz);
writel_relaxed(1, base + QUP_SW_RESET);
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
if (ret) {
dev_err(dev, "cannot set RESET state\n");
goto error;
}
writel_relaxed(0, base + QUP_OPERATIONAL);
writel_relaxed(0, base + QUP_IO_M_MODES);
if (!controller->qup_v1)
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
base + SPI_ERROR_FLAGS_EN);
/* if earlier version of the QUP, disable INPUT_OVERRUN */
if (controller->qup_v1)
writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
base + QUP_ERROR_FLAGS_EN);
writel_relaxed(0, base + SPI_CONFIG);
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
IRQF_TRIGGER_HIGH, pdev->name, controller);
if (ret)
goto error;
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_spi_register_master(dev, master);   //spi_register_master, 注册spi_master设备
if (ret)
goto disable_pm;
return 0;
disable_pm:
pm_runtime_disable(&pdev->dev);
error:
clk_disable_unprepare(cclk);
clk_disable_unprepare(iclk);
spi_master_put(master);
return ret;
}

spi_master注册函数展开如下: spi.c

代码语言:javascript
复制
int devm_spi_register_master(struct device *dev, struct spi_master *master)
{
struct spi_master **ptr;
int ret;
ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = spi_register_master(master);
if (!ret) {
*ptr = master;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return ret;
}
int spi_register_master(struct spi_master *master)
{
static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device		*dev = master->dev.parent;
struct boardinfo	*bi;
int			status = -ENODEV;
int			dynamic = 0;
if (!dev)
return -ENODEV;
status = of_spi_register_master(master);
if (status)
return status;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == 0)
return -EINVAL;
if ((master->bus_num < 0) && master->dev.of_node)
master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
/* convention:  dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
/* FIXME switch to an IDR based scheme, something like
* I2C now uses, so we can't run out of "dynamic" IDs
*/
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
init_completion(&master->xfer_completion);
if (!master->max_dma_len)
master->max_dma_len = INT_MAX;
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev);    //设备注册
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
/* If we're using a queued driver, start the queue */
if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master);  //队列,工作线程初始化   
if (status) {
device_del(&master->dev);
goto done;
}
}
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);   //将spi_master加入spi_master_list
list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info);  //扫描board_list,对已经在list中的此master对应的slave设备进行注册
mutex_unlock(&board_lock);
/* Register devices from the device tree and ACPI */
of_register_spi_devices(master);   //对dt中声明的此master对应的slave设备进行注册
acpi_register_spi_devices(master);
done:
return status;
}

至此,通过device_add函数,spi_master注册到内核。

3 总线传输

spi的总线传输分为同步传输和异步传输,同步传输较简单适合有少量数据的传输,异步传输较复杂,适合对大量数据的传输。本节对异步传输进行解释。总线传输涉及到几个重要的结构体,队列,内核工作线程和厂商的总线传输实现几个方面。

3.1 数据结构

transfer使用的几个链表数据结构的关系如下,它们是spi_mater, spi_message和spi_transfer。

spi_struct
spi_struct
3.2 队列&内核工作线程

spi的总线传输是以spi_master->transfer进行实际的传输的。在实际传输之前要对结构体,队列,工作线程进行初始化。它们的初始化是在spi_master注册函数spi_register_master中调用 spi_master_initialize_queue实现的。spi_master_initialize_queue 实现spi_master成员函数transfer, transfer_one_message,queue和内核工作线程初始化。 spi.c

代码语言:javascript
复制
static int spi_master_initialize_queue(struct spi_master *master)
{ 

int ret;
master->transfer = spi_queued_transfer;           //msater->transfer初始化
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;   //master->transfer_one_message初始化
/* Initialize and start queue */
ret = spi_init_queue(master);   //master->queue和master->kworker初始化
if (ret) { 

dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
master->queued = true;
ret = spi_start_queue(master);
if (ret) { 

dev_err(&master->dev, "problem starting queue\n");
goto err_start_queue;
}
return 0;
}

transfer函数只是将上层传来的spi_message挂到一个链表上,然后就返回了,发送的调度是以kthread_worker和kthread_work机制实现的。worker通过创建一个内核线程,来串行执行kthread_work调用queue去排队的func。spi_init_queue实现了队列和工作线程的初始化。

spi.c

代码语言:javascript
复制
static int spi_init_queue(struct spi_master *master)
{
INIT_LIST_HEAD(&master->queue);
spin_lock_init(&master->queue_lock);
...
init_kthread_worker(&master->kworker); //初始化worker
master->kworker_task = kthread_run(kthread_worker_fn,
&master->kworker, "%s",
dev_name(&master->dev)); //worker线程的函数是kthread_worker_fn
if (IS_ERR(master->kworker_task)) {
dev_err(&master->dev, "failed to create message pump task\n");
return -ENOMEM;
}
init_kthread_work(&master->pump_messages, spi_pump_messages); //初始化work,定义work->func
}

transfer函数进行了入队操作,实现如下: spi.c

代码语言:javascript
复制
static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
...
list_add_tail(&msg->queue, &master->queue);
if (!master->busy)
queue_kthread_work(&master->kworker, &master->pump_messages); //将work添加到worker的list中
...
}

kthread_worker中真正的func函数spi_pump_message实现了出队操作: spi.c

代码语言:javascript
复制
static void spi_pump_messages(struct kthread_work *work)
{
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
...
/* Extract head of queue */
master->cur_msg =
list_first_entry(&master->queue, struct spi_message, queue);    //从队列头取得spi_message
list_del_init(&master->cur_msg->queue);
...
if (!was_busy && master->prepare_transfer_hardware) {        //未实现,pass
ret = master->prepare_transfer_hardware(master);
if (ret) {
dev_err(&master->dev,
"failed to prepare transfer hardware\n");
if (master->auto_runtime_pm)
pm_runtime_put(master->dev.parent);
return;
}
}
trace_spi_message_start(master->cur_msg);
if (master->prepare_message) {   //未实现,pass
ret = master->prepare_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to prepare message: %d\n", ret);
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
master->cur_msg_prepared = true;
}
ret = spi_map_msg(master, master->cur_msg);  //未实现,pass
if (ret) {
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
ret = master->transfer_one_message(master, master->cur_msg);  
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
return;
}
}

transfer_one_message实现如下,扫描msg->transfers为列表头的队列,调用master->transfer_one进行发送。transfer_one_message的发送数据结构是struct spi_message,transfer的发送数据结构是spi_transfer。 spi.c

代码语言:javascript
复制
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;
spi_set_cs(msg->spi, true);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);
if (xfer->tx_buf || xfer->rx_buf) {
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 += 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;
}
} else {
if (xfer->len)
dev_err(&msg->spi->dev,
"Bufferless transfer has length %u\n",
xfer->len);
}
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;
}

spi_transfer在spi-qup.c的probe函数中已经进行了赋值,因此最终的transfer依赖于控制器厂商的实现。

异步传输函数spi_async是以spi_async—-> __spi_async—->master->transfer关系调用transfer函数的。

3.3 厂商的总线传输

控制器驱动注册函数中进行了传输函数的赋值。

代码语言:javascript
复制
master->transfer_one = spi_qup_transfer_one;

spi_qup_transfer_one的实现如下,涉及到控制器状态机的控制和向fifo写数据等寄存器操作。如果对此函数感兴趣可以查看qcom的spec。 spi_qup.c

代码语言:javascript
复制
static int spi_qup_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct spi_qup *controller = spi_master_get_devdata(master);
unsigned long timeout, flags;
int ret = -EIO;
ret = spi_qup_io_config(spi, xfer);
if (ret)
return ret;
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
timeout = 100 * msecs_to_jiffies(timeout);
reinit_completion(&controller->done);
spin_lock_irqsave(&controller->lock, flags);
controller->xfer     = xfer;
controller->error    = 0;
controller->rx_bytes = 0;
controller->tx_bytes = 0;
spin_unlock_irqrestore(&controller->lock, flags);
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
dev_warn(controller->dev, "cannot set RUN state\n");
goto exit;
}
if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
dev_warn(controller->dev, "cannot set PAUSE state\n");
goto exit;
}
spi_qup_fifo_write(controller, xfer);
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
dev_warn(controller->dev, "cannot set EXECUTE state\n");
goto exit;
}
if (!wait_for_completion_timeout(&controller->done, timeout))
ret = -ETIMEDOUT;
exit:
spi_qup_set_state(controller, QUP_STATE_RESET);
spin_lock_irqsave(&controller->lock, flags);
controller->xfer = NULL;
if (!ret)
ret = controller->error;
spin_unlock_irqrestore(&controller->lock, flags);
return ret;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/180537.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 控制器设备注册
  • 2 控制器驱动注册
  • 3 总线传输
    • 3.1 数据结构
      • 3.2 队列&内核工作线程
        • 3.3 厂商的总线传输
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档