drivers/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;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
return 0;
}
spi 总线驱动主要结构体
对应的节点:/sys/bus/spi
struct bus_type spi_bus_type = {
.name = "spi", //总线的名字
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
spi平台(总线层)对应的主要结构体
对应的节点:/sys/class/spi_master
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
.dev_groups = spi_master_groups, //其节点下有对应控制接口
};
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev); //通过device指针获取对应的spi_device 指针
const struct spi_driver *sdrv = to_spi_driver(drv); //通过device_driver指针获取对应的spi_driver 指针
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv)) //如果spi_driver的of_device_id存在,则通过设备节点的compatible来匹配
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
struct spi_device {
struct device dev; //device 设备
struct spi_master *master; //spi设备的更高层描述,每一个spi控制器就对应一个master,一个spi设备必须对应一个master,master下可以有多个spi设备。
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word; //如果传输是以字节为单位的话就设置为8,相应地,如果是以2个字节为单位则设置为16。
u16 mode;
#define SPI_CPHA 0x01 /* clock phase 时钟极性*/
#define SPI_CPOL 0x02 /* clock polarity 时钟相位*/
#define SPI_MODE_0 (0|0) /* (original MicroWire) */ //四种模式,上篇文章有描述
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state; //一般设置为null
void *controller_data; //数据
char modalias[SPI_NAME_SIZE]; //,一般来说设备与驱动能否匹配起来就要看它,注意,这里只是说一般,因为还有另外两种匹配的方法,不过大部分设备和驱动能否匹配起来都是根据名字是否相等,当然像USB驱动就不是根据名字来匹配的,而是根据与id table。
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
对于具体的平台,nand、iic这些都是平台设备,spi当然也一样是平台设备,对于平台设备,大部分情况下是先注册设备再注册驱动。
spi1: spi@ff1d8000 {
compatible = "rockchip,px30-spi", "rockchip,rk3066-spi";
static int rockchip_spi_probe(struct platform_device *pdev)
{
int ret = 0;
struct rockchip_spi *rs;
struct spi_master *master;
struct resource *mem;
u32 rsd_nsecs;
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
if (!master)
return -ENOMEM;
platform_set_drvdata(pdev, master);
rs = spi_master_get_devdata(master);
/* Get basic io resource and map it */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取资源
rs->regs = devm_ioremap_resource(&pdev->dev, mem); //映射资源
if (IS_ERR(rs->regs)) {
ret = PTR_ERR(rs->regs);
goto err_ioremap_resource;
}
rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(rs->apb_pclk)) {
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
ret = PTR_ERR(rs->apb_pclk);
goto err_ioremap_resource;
}
rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
if (IS_ERR(rs->spiclk)) {
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
ret = PTR_ERR(rs->spiclk);
goto err_ioremap_resource;
}
ret = clk_prepare_enable(rs->apb_pclk); //使能外围时钟("apb_pclk" for the peripheral clock.)
if (ret) {
dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
goto err_ioremap_resource;
}
ret = clk_prepare_enable(rs->spiclk);//使能输出时钟("spiclk" for the transfer-clock)
if (ret) {
dev_err(&pdev->dev, "Failed to enable spi_clk\n");
goto err_spiclk_enable;
}
spi_enable_chip(rs, 0);
rs->type = SSI_MOTO_SPI;
rs->master = master;
rs->dev = &pdev->dev;
rs->max_freq = clk_get_rate(rs->spiclk);
if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
&rsd_nsecs))
rs->rsd_nsecs = rsd_nsecs;
rs->fifo_len = get_fifo_len(rs);
if (!rs->fifo_len) {
dev_err(&pdev->dev, "Failed to get fifo length\n");
ret = -EINVAL;
goto err_get_fifo_len;
}
spin_lock_init(&rs->lock);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
master->auto_runtime_pm = true;
master->bus_num = pdev->id;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; //设置spi模式 时钟低电平时有效, 数据采样发生在时钟(SCK)偶数(2,4,6,...,16)边沿
master->num_chipselect = 2;
master->dev.of_node = pdev->dev.of_node;
master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
master->set_cs = rockchip_spi_set_cs;
master->prepare_message = rockchip_spi_prepare_message;
master->unprepare_message = rockchip_spi_unprepare_message;
master->transfer_one = rockchip_spi_transfer_one; //做slave的时候,需要做此函数设置
master->handle_err = rockchip_spi_handle_err;
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
if (IS_ERR_OR_NULL(rs->dma_tx.ch)) {
/* Check tx to see if we need defer probing driver */
if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_get_fifo_len;
}
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
}
rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");
if (!rs->dma_rx.ch) {
if (rs->dma_tx.ch) {
dma_release_channel(rs->dma_tx.ch);
rs->dma_tx.ch = NULL;
}
dev_warn(rs->dev, "Failed to request RX DMA channel\n");
}
if (rs->dma_tx.ch && rs->dma_rx.ch) {
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
rs->dma_tx.direction = DMA_MEM_TO_DEV;
rs->dma_rx.direction = DMA_DEV_TO_MEM;
master->can_dma = rockchip_spi_can_dma;
master->dma_tx = rs->dma_tx.ch;
master->dma_rx = rs->dma_rx.ch;
}
rs->high_speed_state = pinctrl_lookup_state(rs->dev->pins->p,
"high_speed");
if (IS_ERR_OR_NULL(rs->high_speed_state)) {
dev_warn(&pdev->dev, "no high_speed pinctrl state\n");
rs->high_speed_state = NULL;
}
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "Failed to register master\n");
goto err_register_master;
}
return 0;
}
&spi1 { 引用spi 控制器节点
status = "okay";
max-freq = <48000000>; spi内部工作时钟
dma-names = "tx","rx"; 使能DMA模式,一般通讯字节少于32字节的不建议用
spi_rk1608@00 {
compatible = "rockchip,rk1608"; 与驱动对应的名字
reg = <0>; 片选0或者1
spi-max-frequency = <24000000>; spi clk输出的时钟频率,不超过50M
spi-cpha; 如果有配,cpha为1
spi-cpol; 如果有配,cpol为1,clk脚保持高电平
status = "okay"; 使能设备节点
};
};
spi-max-frequency 是 SPI 的输出时钟,是 max-freq 分频后输出的,关系是 max-freq >= 2*spimax-frequency。
static struct spi_driver rk1608_driver = {
.driver = {
.of_match_table = of_match_ptr(rk1608_of_match),
.name = "rk1608",
},
.probe = rk1608_probe,
.remove = rk1608_remove,
.id_table = rk1608_id,
};
static int rk1608_probe(struct spi_device *spi)
{
rk1608_parse_dt_property //解析dts数据
reset_gpio //高->低
irq_gpio //必须有
wakeup //可以没有,he gpio pin to be used for waking up the controller
frequency //速率
解析dts 中rk1608的配置信息保存到struct rk1608_state *rk1608;
rk1608_get_remote_node_dev
rk1608_get_remote_node_dev
of_graph_get_remote_node //找到remote-endpoint节点信息保存到rk1608_state *rk1608
rk1608_initialize_controls //初始化v4l2_ctrl_handler
v4l2_spi_subdev_init
v4l2_subdev_init
devm_request_threaded_irq
rk1608_threaded_isr,
rk1608_msq_recv_msg // receive a msg from RK1608 -> AP msg queue 第一个参数msg queue,第二个参数a msg pointer buf.写相应结存器,接受rk1608的信息.
rk1608_dev_register //调用到kernel/drivers/media/spi/rk1608_dev.c
pdata->misc.name = "rk_preisp";//名字data->misc.name = "rk_preisp",上层操作打开的节点
pdata->misc.fops = &rk1608_fops; //操作函数
static const struct file_operations rk1608_fops = {
.owner = THIS_MODULE,
.open = rk1608_dev_open,
*pdata =container_of(file->private_data, struct rk1608_state, misc);//pdata指向msic的首地址
rk1608_set_power(pdata, 1);
rk1608_power_on(pdata); //设置片选,复位,设置spi速度
rk1608_lsb_w32 //传输cmd ,addr,data.
rk1608_hw_init //硬件初始化
rk1608_download_fw //下载rk1608的固件,dts配置的固件名字
enable_irq //使能中断
rk1608_set_log_level
rk1608_send_msg_to_dsp //send a msg to Soc->DSP msg queue
.release = rk1608_dev_release,
.write = rk1608_dev_write,
copy_from_user //从用户空间copy
.poll = rk1608_dev_poll,
.unlocked_ioctl = rk1608_dev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = rk1608_compat_ioctl,
#endif
};
misc_register(&pdata->misc); //注册字符设备
}