i.mx536(cotex-a8核)的I2C驅動理解一(probe)

//總得來說三個主要步驟
//(1)映射虛擬內存,註冊中斷等
//(2)填充結構體struct imx_i2c_struct
//(3)調用i2c_register_adapter註冊I2C設備
static int __init i2c_imx_probe(struct platform_device *pdev)
{
	//分析了幾個驅動發現,平臺驅動有很多相似的地方,比如說在prode裏一般都會定義
	//一個platform_data結構體,一個對應設備的結構體比如struct imx_i2c_struct
	struct imx_i2c_struct *i2c_imx;
	struct resource *res;
	struct imxi2c_platform_data *pdata;
	//__iomem表示指針是指向一個I/O的內存空間
	void __iomem *base;
	//resource_size_t = u32
	resource_size_t res_size;
	int irq;
	int ret;

	dev_dbg(&pdev->dev, "<%s>\n", __func__);
	//獲取IO資源和中斷資源
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "can't get device resources\n");
		return -ENOENT;
	}
	//獲取中斷資源
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "can't get irq number\n");
		return -ENOENT;
	}

	pdata = pdev->dev.platform_data;

	if (pdata && pdata->init) {
		ret = pdata->init(&pdev->dev);
		if (ret)
			return ret;
	}

	res_size = resource_size(res);
	//檢測該IO資源是否可用,若可用,並標誌爲已用
	if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
		ret = -EBUSY;
		goto fail0;
	}
	//映射物理地址到虛擬內存
	base = ioremap(res->start, res_size);
	if (!base) {
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = -EIO;
		goto fail1;
	}
	//給i2c_imx分配內存,並初始化爲0
	i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
	if (!i2c_imx) {
		dev_err(&pdev->dev, "can't allocate interface\n");
		ret = -ENOMEM;
		goto fail2;
	}

	
	//填充結構體i2c_imx
	strcpy(i2c_imx->adapter.name, pdev->name);
	//i2c_adapter,Linux的I2C驅動框架中的主要數據結構(i2c_driver、i2c_client、i2c_adapter和i2c_algorithm)之一出現了
	//對應於物理上的一個適配器
	i2c_imx->adapter.owner		= THIS_MODULE;
	//i2c_algorithm也出現了
	//i2c_algorithm主要提供通信的函數
	i2c_imx->adapter.algo		= &i2c_imx_algo;
	i2c_imx->adapter.dev.parent	= &pdev->dev;
	i2c_imx->adapter.nr 		= pdev->id;
	i2c_imx->irq			= irq;
	i2c_imx->base			= base;
	i2c_imx->res			= res;

	/* Get I2C clock */
	//開啓I2C時鐘源
	i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
	if (IS_ERR(i2c_imx->clk)) {
		ret = PTR_ERR(i2c_imx->clk);
		dev_err(&pdev->dev, "can't get I2C clock\n");
		goto fail3;
	}

	/* Request IRQ */
	//註冊中斷函數i2c_imx_isr
	ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
	if (ret) {
		dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
		goto fail4;
	}

	/* Init queue */
	//初始化queue
	init_waitqueue_head(&i2c_imx->queue);

	/* Set up adapter data */
	//把I2C_imx保存到adapter中,最終可以調用i2c_get_adapdata獲取
	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

	/* Set up clock divider */
	//這個pdata->bitrate在板級配置文件中設置
	if (pdata && pdata->bitrate)
		i2c_imx_set_clk(i2c_imx, pdata->bitrate);
	else
		i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);

	/* Set up chip registers to defaults */
	writeb(0, i2c_imx->base + IMX_I2C_I2CR);
	writeb(0, i2c_imx->base + IMX_I2C_I2SR);

	/* Add I2C adapter */
	//重要的函數來了,
	//i2c_add_numbered_adapter最終調用i2c_register_adapter添加I2C控制器
	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
	if (ret < 0) {
		dev_err(&pdev->dev, "registration failed\n");
		goto fail5;
	}

	/* Set up platform driver data */
	platform_set_drvdata(pdev, i2c_imx);
	//打印調試信息
	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
	dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
		i2c_imx->res->start, i2c_imx->res->end);
	dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
		res_size, i2c_imx->res->start);
	dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
		i2c_imx->adapter.name);
	dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

	return 0;   /* Return OK */

fail5:
	free_irq(i2c_imx->irq, i2c_imx);
fail4:
	clk_put(i2c_imx->clk);
fail3:
	kfree(i2c_imx);
fail2:
	iounmap(base);
fail1:
	release_mem_region(res->start, resource_size(res));
fail0:
	if (pdata && pdata->exit)
		pdata->exit(&pdev->dev);
	return ret; /* Return error number */
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章