Linux:4.6
应用开发板:zynq系列 zc706、zedboard
文件系统:ubuntu12
参考帖子:https://stackoverflow.com/questions/53634892/linux-spidev-why-it-shouldnt-be-directly-in-devicetree
之前实验过spi控制器下面挂载spi设备,当时,关于spi设备树的节点描述如下:
spi@e0007000 {
compatible = "xlnx,zynq-spi-r1p6";
reg = <0xe0007000 0x1000>;
status = "okay";
interrupt-parent = <0x3>;
interrupts = <0x0 0x31 0x4>;
clocks = <0x1 0x1a 0x1 0x23>;
clock-names = "ref_clk", "pclk";
#address-cells = <0x1>;
#size-cells = <0x0>;
num-cs = <0x1>;
device@0{
compatible = "spidev";
reg = <0x0>;
spi-max-frequency = <0x100000>;
};
};
这次在在上述板子上,依然按照上面的方法忽然就不行了,报错信息如下:
spidev spi32766.0: buggy DT: spidev listed directly in DT
------------[ cut here ]------------
WARNING: CPU: 0 PID: 1 at drivers/spi/spidev.c:719 spidev_probe+0x16c/0x1a0
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.6.0 #21
Hardware name: Xilinx Zynq Platform
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
---[ end trace 799ecf1d804db820 ]---
问题分析:
根据我的参考帖子,研究了一下,应该是不能直接使用 驱动名:“spidev”。
方法1:
查询spidev.c的源码后,把设备树中使用的"spidev" 替换为 "rohm,dh2228fv",就成功加载了。
方法2:
帖子里还教了另外的方法,在 spidev_dt_ids里,添加自己的驱动名,比如"myspidriver"。
在设备树里使用自己定义的名字。
最后设备树如下:
spi@e0007000 {
compatible = "xlnx,zynq-spi-r1p6";
reg = <0xe0007000 0x1000>;
status = "okay";
interrupt-parent = <0x3>;
interrupts = <0x0 0x31 0x4>;
clocks = <0x1 0x1a 0x1 0x23>;
clock-names = "ref_clk", "pclk";
#address-cells = <0x1>;
#size-cells = <0x0>;
num-cs = <0x1>;
device@0{
compatible = "rohm,dh2228fv";
reg = <0x0>;
spi-max-frequency = <0x100000>;
};
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 参 考 代 码 开 始 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
spidev.c 的685行开始:
#ifdef CONFIG_OF
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
// { .compatible = "myspidriver" }, 这里可以加自命名驱动,注意:内核编译要满足#ifdef CONFIG_OF条件
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
#endif
/*-------------------------------------------------------------------------*/
static int spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
/*
* spidev should never be referenced in DT without a specific
* compatible string, it is a Linux implementation thing
* rather than a description of the hardware.
*/
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); //报错的地方
WARN_ON(spi->dev.of_node &&
!of_match_device(spidev_dt_ids, &spi->dev));
}
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
if (!spidev)
return -ENOMEM;
/* Initialize the driver data */
spidev->spi = spi;
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
/* If we can allocate a minor number, hook up this device.
* Reusing minors is fine so long as udev or mdev is working.
*/
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS);
if (minor < N_SPI_MINORS) {
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
status = PTR_ERR_OR_ZERO(dev);
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) {
set_bit(minor, minors);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
spidev->speed_hz = spi->max_speed_hz;
if (status == 0)
spi_set_drvdata(spi, spidev);
else
kfree(spidev);
return status;
}
static int spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi);
/* make sure ops on existing fds can abort cleanly */
spin_lock_irq(&spidev->spi_lock);
spidev->spi = NULL;
spin_unlock_irq(&spidev->spi_lock);
/* prevent new opens */
mutex_lock(&device_list_lock);
list_del(&spidev->device_entry);
device_destroy(spidev_class, spidev->devt);
clear_bit(MINOR(spidev->devt), minors);
if (spidev->users == 0)
kfree(spidev);
mutex_unlock(&device_list_lock);
return 0;
}
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
.of_match_table = of_match_ptr(spidev_dt_ids),
},
.probe = spidev_probe,
.remove = spidev_remove,
/* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handles the rest.
*/
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~