1. 程式人生 > >linux spi裝置驅動中probe函式何時被呼叫

linux spi裝置驅動中probe函式何時被呼叫

這兩天被裝置檔案快搞瘋了,也怪自己學東西一知半解吧,弄了幾天總算能把設備註冊理清楚一點點了。就以spi子裝置的註冊為例總結一下,免得自己忘記。

首先以註冊一個spidev的裝置為例:

static struct spi_board_info imx5_spi_printer_device[] __initdata = 
{
	{
		.modalias = "spidev",
		.max_speed_hz = 8000000, 	
		.bus_num = 1, 
		.chip_select = 1,
		.mode = SPI_MODE_0,				
	},	
};
spi_register_board_info(imx5_spi_printer_device,ARRAY_SIZE(imx5_spi_printer_device));

在mx5_loco.c檔案中新增上面結構體spi_board_info,modalias必須指定已有的一個驅動,至於bus_num和chip_select,如果你不知道bus_num是多少,可以在你的父驅動中打印出來,這裡的bus_num一定要和父類的bus_num一致,否則是無法生成裝置檔案的。如果spi一直沒有時鐘訊號,很有可能是bus_num不對。

這樣系統起來之後就會在/dev目錄下出現一個名為spidev1.1的裝置檔案,讀寫這個檔案就可以實現spi的操作

還有下面這種情況:

static struct spi_board_info prt_spi_device[] __initdata = {
    {
     .modalias = "HotPRT",
     .max_speed_hz = 12500000,	/* max spi clock (SCK) speed in HZ */
     .bus_num = 1,
     .chip_select = 1,
//     .mode = SPI_MODE_0,
     .platform_data = 0,
     },
};
spi_register_board_info(prt_spi_device, ARRAY_SIZE(prt_spi_device));

我自己實現了一個spi的驅動,然後需要建立一個裝置檔案,裝置檔案的建立是在probe中完成。
static struct spi_driver prt_driver = {
	.driver = {
		.name	= "HotPRT",
		.bus	= &spi_bus_type,
		.owner	= THIS_MODULE,
	},
	.probe	= prt_probe,
	.remove	= __devexit_p(prt_remove),
};
spi_register_driver(&prt_driver);
但是我開始一直觸發不了probe,於是找啊找,總算知道probe的呼叫過程了,如下:
int spi_register_driver(struct spi_driver *sdrv)
{
	sdrv->driver.bus = &spi_bus_type;
	if (sdrv->probe)
		sdrv->driver.probe = spi_drv_probe;
	if (sdrv->remove)
		sdrv->driver.remove = spi_drv_remove;
	if (sdrv->shutdown)
		sdrv->driver.shutdown = spi_drv_shutdown;
	return driver_register(&sdrv->driver);
}

然後呼叫driver_register

<pre name="code" class="cpp">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) {
		put_driver(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;
}

直接看bus_add_driver
	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;

	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	module_add_driver(drv->owner, drv);

這裡只擷取一部分,最後呼叫的是driver_attach
int driver_attach(struct device_driver * drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

真正起作用的是__driver_attach:

static int __driver_attach(struct device * dev, void * data)
{
。。。
if (!dev->driver)
   driver_probe_device(drv, dev);
。。。
}


int driver_probe_device(struct device_driver * drv, struct device * dev)
{
。。。
//1.先是判斷bus是否match:
if (drv->bus->match && !drv->bus->match(dev, drv))
   goto done;
//2.再具體執行probe:
ret = really_probe(dev, drv);
。。。
}

really_probe才是我們要找的函式:
static int really_probe(struct device *dev, struct device_driver *drv)
{
。。。
//1.先是呼叫的驅動所屬匯流排的probe函式:
if (dev->bus->probe) {
   ret = dev->bus->probe(dev);
   if (ret)
    goto probe_failed;

} else if (drv->probe) {
//2.再呼叫你的驅動中的probe函式:
   ret = drv->probe(dev);
   if (ret)
    goto probe_failed;
}
。。。
}

其中,drv->probe(dev),才是真正呼叫你的驅動實現的具體的probe函式。至此probe函式被呼叫。

在板檔案中新增spi_board_info,並在板檔案的init函式中呼叫spi_register_board_info(

prt_spi_device<span style="font-family: NSimSum; line-height: 1.5; ">,ARRAY_SIZE(</span><span style="font-family: NSimSum; ">prt_spi_device</span><span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 1.5; "><span style="line-height: 1.5; font-family: NSimSum; ">))</span></span><span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 1.5; ">;</span>

//註冊spi_board_info。這個程式碼會把spi_board_info註冊到連結串列board_list上。spi_device封裝了一個spi_master結構體,事實上spi_master的註冊會在spi_register_board_info之後,spi_master註冊的過程中會呼叫scan_boardinfo掃描board_list,找到掛接在它上面的spi裝置,然後建立並註冊spi_device。

另外有關spi片選引腳的設定:1、直接將gpio配置成spi片選功能引腳。 2、將gpio配置成片選引腳,這個時候就需要設定結構體

static void mx53_loco_gpio_spi_chipselect_active(int cspi_mode, int status,
					     int chipselect)
{
	switch (cspi_mode) {
    case 1: //ESPI1,bus_num 1
		switch (chipselect) {
        case 0x1://SS0	chipselect = .chip_select + 1
			{
                iomux_v3_cfg_t cspi_ss0 = MX53_PAD_CSI0_DAT7__ECSPI1_SS0;
                iomux_v3_cfg_t cspi_ss2 = MX53_PAD_KEY_ROW2__GPIO4_11;//SS2


                mxc_iomux_v3_setup_pad(cspi_ss0);
                mxc_iomux_v3_setup_pad(cspi_ss2);


                gpio_request(ECSPI1_CS2, "ecspi-cs2");
                gpio_direction_input(ECSPI1_CS2);
			}
            break;
		default:
			break;
		}
    case 2://ESPI2,bus_num 2
		switch (chipselect) {
       	case 0x2://SS0
            {
                gpio_request(ECSPI2_SS1, "ecspi-cs1");
                gpio_direction_output(ECSPI2_SS1, 1);
            }
            break;
        case 0x3://SS1
			{
                gpio_request(ECSPI2_SS1, "ecspi-cs1");
                gpio_direction_output(ECSPI2_SS1, 0);
			}
			break;
		default:
			break;
		}
	default:
		break;
	}
}

static void mx53_loco_gpio_spi_chipselect_inactive(int cspi_mode, int status,
					       int chipselect)
{
	switch (cspi_mode) {
	case 1:
		switch (chipselect) {
		case 0x1:
			gpio_free(ECSPI1_CS2);
			break;
		default:
			break;
		}
	case 2:
		switch (chipselect) {
       	case 0x2:
            {
              	gpio_request(ECSPI2_SS1, "ecspi-cs1");
              	gpio_direction_output(ECSPI2_SS1, 0);
            }
            break;
		case 0x3:
			{
              	gpio_request(ECSPI2_SS1, "ecspi-cs1");
              	gpio_direction_output(ECSPI2_SS1, 1);
			}
			break;
		default:
			break;
		}
	default:
		break;
	}
}

static struct mxc_spi_master mxcspi_data = {

	.maxchipselect = 4,
	.spi_version = 23,
	.chipselect_active = mx53_loco_gpio_spi_chipselect_active,
	.chipselect_inactive = mx53_loco_gpio_spi_chipselect_inactive,
};
這樣設定後就不再需要手動設定片選的狀態了。

裝置檔案的生成:

1、用mknod手動生成

通過cat /proc/devices命令,可以看到主裝置的編號,例如spi是153 ,如果想生成一個spi的子裝置可以用 mknod /dev/spidev -c 153 1

mknod 裝置名 裝置型別 主裝置號 子裝置號

2、在驅動中就加入建立裝置檔案的程式碼:

	struct device *devi;
	prt_class = class_create(THIS_MODULE, PRT_DEV_NAME);
	if(IS_ERR(prt_class))
		PTR_ERR(prt_class);
	
	devi = device_create(prt_class,NULL,MKDEV(PRT_DEV_MAJOR, 1), NULL, PRT_DEV_NAME);
	if(IS_ERR(devi))
		PTR_ERR(devi);
首先class_create用它來建立一個類,這個類存放於sysfs下面,再呼叫device_create(…)函式來在/dev目錄下建立相應的裝置節點。