linux spi主机控制器pl022驱动注册以及匹配设备过程
最近看海思的spi比较多,海思3519的spi ip使用的时ARM提供的pl022,这里对pl022驱动注册和匹配设备树中的设备这个过程捋一下。pl022是ARM提供的片内外设,很多厂商都用了这个ip,只在一些细小的区别。所以它的驱动也是非常通用的。pl022的手册可以看这里点击打开链接我们需要首先了解amba总线。本段摘自https://blog.csdn.net/yuanlulu/articl
最近看海思的spi比较多,海思3519的spi ip使用的时ARM提供的pl022,这里对pl022驱动注册和匹配设备树中的设备这个过程捋一下。
pl022是ARM提供的片内外设,很多厂商都用了这个ip,只在一些细小的区别。所以它的驱动也是非常通用的。pl022的手册可以看这里点击打开链接
我们需要首先了解amba总线。本段摘自https://blog.csdn.net/yuanlulu/article/details/7339836
*
* This device type deals with ARM PrimeCells and anything else that
* presents a proper CID ( 0xB105F00D ) at the end of the I/O register
* region or that is derived from a PrimeCell.
下面来看一下amba相关的数据结构
struct amba_id {
unsigned int id;
unsigned int mask;
void *data;
};
amba id 结构,这个id是用来匹配amba 驱动和amba设备的。
struct amba_driver {
struct device_driver drv;
int (*probe)(struct amba_device *, const struct amba_id *);
int (*remove)(struct amba_device *);
void (*shutdown)(struct amba_device *);
int (*suspend)(struct amba_device *, pm_message_t);
int (*resume)(struct amba_device *);
const struct amba_id *id_table;
};
几个方法是驱动中常见的方法,最后这个参数比较重要,是一个id表,表示的是这个驱动所支持的设备表。以此来支持多款设备的相同ip。
struct amba_device {
struct device dev;
struct resource res;
struct clk *pclk;
unsigned int periphid;
unsigned int irq[AMBA_NR_IRQS];
};
amba设备结构体
接口:
int amba_driver_register(struct amba_driver *);
驱动注册函数,注册时会去匹配是否有对应的设备已经存在,存在的话就会执行amba_driver中的probe函数。匹配就是通过刚才说的amba_id中的id参数。我们来追一下这个过程。
int amba_driver_register(struct amba_driver *drv)
{
drv->drv.bus = &amba_bustype;
#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
SETFN(probe);
SETFN(remove);
SETFN(shutdown);
return driver_register(&drv->drv); //驱动注册
}
进入驱动注册函数
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv); //添加驱动
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
驱动添加函数
/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.
*/
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv); //继续追这里
if (error)
goto out_unregister;
}
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
这里会遍历每一个设备进行匹配
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
进入__driver_attch函数中
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev)) //进行匹配
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev); //执行probe方法
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
我们来看看是如何匹配的
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
我们得找到amba总线上匹配函数。匹配函数是在之前的amba_driver_register中赋值的
int amba_driver_register(struct amba_driver *drv)
{
drv->drv.bus = &amba_bustype;
#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
SETFN(probe);
SETFN(remove);
SETFN(shutdown);
return driver_register(&drv->drv);
}
从这里找到真正的匹配函数是amba_match
struct bus_type amba_bustype = {
.name = "amba",
.dev_attrs = amba_dev_attrs,
.match = amba_match, //匹配函数
.uevent = amba_uevent,
.pm = &amba_pm,
};
看看这个amba_match的实现
static int amba_match(struct device *dev, struct device_driver *drv)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *pcdrv = to_amba_driver(drv);
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
}
amba_lookup是匹配的过程
static const struct amba_id *
amba_lookup(const struct amba_id *table, struct amba_device *dev)
{
int ret = 0;
while (table->mask) {
ret = (dev->periphid & table->mask) == table->id;
if (ret)
break;
table++;
}
return ret ? table : NULL;
}
看来实现还是很简单的,就是amba_dev->periphid和table->id在比较。到这里设备和驱动就算是匹配上了。
匹配上之后需要找到要执行的probe函数,继续回到static int __driver_attach(struct device *dev, void *data)函数中,匹配成功后寻找probe函数
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv); //继续寻找
pm_request_idle(dev);
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
int local_trigger_count = atomic_read(&deferred_trigger_count);
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto probe_failed;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) { //设备总线中注册了probe函数
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) { //设备总线没有注册probe函数,查看驱动中是否注册了probe函数
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
/* Did a trigger occur while probing? Need to re-trigger if yes */
if (local_trigger_count != atomic_read(&deferred_trigger_count))
driver_deferred_probe_trigger();
} else if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
} else {
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
整个的路径:driver_register -> bus_add_driver -> driver_attach -> __driver_attach(对每个设备) -> driver_probe_device -> drv->bus->match(dev, drv)(检查这个设备是否与新注册的驱动匹配) -> really_probe -> dev->bus->probe(dev)(如果存在) (否则) -> drv->probe(dev)。
对于pl022驱动来讲,我们看看它的具体情况
首先是要实现一个amba_driver
static struct amba_driver pl022_driver = {
.drv = {
.name = "ssp-pl022",
.pm = &pl022_dev_pm_ops,
},
.id_table = pl022_ids,
.probe = pl022_probe,
.remove = pl022_remove,
};
看一看它的pl022_ids,table表的最后一定要置0,表示结束。
static struct amba_id pl022_ids[] = {
{
/*
* ARM PL022 variant, this has a 16bit wide
* and 8 locations deep TX/RX FIFO
*/
.id = 0x00041022,
.mask = 0x000fffff,
.data = &vendor_arm,
},
{
/*
* ST Micro derivative, this has 32bit wide
* and 32 locations deep TX/RX FIFO
*/
.id = 0x01080022,
.mask = 0xffffffff,
.data = &vendor_st,
},
{
/*
* ST-Ericsson derivative "PL023" (this is not
* an official ARM number), this is a PL022 SSP block
* stripped to SPI mode only, it has 32bit wide
* and 32 locations deep TX/RX FIFO but no extended
* CR0/CR1 register
*/
.id = 0x00080023,
.mask = 0xffffffff,
.data = &vendor_st_pl023,
},
{
/*
* PL022 variant that has a chip select control register whih
* allows control of 5 output signals nCS[0:4].
*/
.id = 0x000b6022,
.mask = 0x000fffff,
.data = &vendor_lsi,
},
{
/*
* Hisilicon derivative, this has a 16bit wide
* and 256 locations deep TX/RX FIFO
*/
.id = 0x00800022,
.mask = 0xffffffff,
.data = &vendor_hisi,
},
{ 0, 0 },
};
最后一个是海思的id。
这个id是和设备树对应的,海思的设备树如下,periphid是和上面的id相互匹配的。
spi_bus0: spi@12120000 {
compatible = "arm,pl0229", "arm,primecell";
arm,primecell-periphid = <0x00800022>;
reg = <0x12120000 0x1000>;
/* dmas = <&dmac 1 &dmac 2>;*/
interrupts = <0 9 4>;
clocks = <&clock HI3519_SPI0_CLK>;
clock-names = "apb_pclk";
dmas = <&dmac 13 1>,<&dmac 12 2>;
dma-names = "tx","rx";
status = "disabled";
num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
};
spi_bus1: spi@12121000 {
compatible = "arm,pl022", "arm,primecell";
arm,primecell-periphid = <0x00800022>;
reg = <0x12121000 0x1000>, <0x12030004 0x4>;
/*dmas = <&dmac 1 &dmac 2>;*/
interrupts = <0 10 4>;
clocks = <&clock HI3519_SPI1_CLK>;
clock-names = "apb_pclk";
dmas = <&dmac 14 1>, <&dmac 15 2>;
dma-names = "tx","rx";
status = "disabled";
num-cs = <2>;
#address-cells = <1>;
#size-cells = <0>;
hisi,spi_cs_sb = <26>;
hisi,spi_cs_mask_bit = <0x0c000000>;
};
spi_bus2: spi@12122000 {
compatible = "arm,pl022", "arm,primecell";
arm,primecell-periphid = <0x00800022>;
reg = <0x12122000 0x1000>;
/* dmas = <&dmac 1 &dmac 2>;*/
interrupts = <0 11 4>;
clocks = <&clock HI3519_SPI2_CLK>;
clock-names = "apb_pclk";
dmas = <&dmac 13 1>,<&dmac 12 2>;
dma-names = "tx","rx";
status = "disabled";
num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
};
spi_bus3: spi@12123000 {
compatible = "arm,pl022", "arm,primecell";
arm,primecell-periphid = <0x00800022>;
reg = <0x12123000 0x1000>;
interrupts = <0 12 4>;
clocks = <&clock HI3519_SPI3_CLK>;
clock-names = "apb_pclk";
dmas = <&dmac 12 1>,<&dmac 13 2>;
dma-names = "tx","rx";
status = "disabled";
num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
};
更多推荐
所有评论(0)