i2c 設備自動探測

最近在做camera和ts的自動識別。因爲平臺上各種品牌的camera和ts種類繁多,而且有的i2c地址有衝突,通常是在board-device裏面配置i2c設備,如果遇到不同機型有相同i2c地址則會造成設備創建失敗,只能在編譯的時候去改代碼,這樣調試多種機型就很繁瑣,所以有了以下代碼。


這裏先看下camera的代碼:

static int gc0309_lookfor_bus(struct gc0309_info *info)
{
	uint8_t val;
	uint32_t ver;

	info->i2c_dev->adapter = i2c_get_adapter(i2c_bus);

	gc0309_i2c_rx_receive(info->i2c_dev, 0x00, &val, 1);
	ver = val;
	cam_notice("read ver=%x\n", ver);

	if (ver == 0xa0) {
		cam_notice("gc0309 sensor detected...\n");
		return 0;
	}
	cam_notice("gc0309 not found on all i2c bus.\n");

	return -ENODEV;
}

static int gc0309_register_i2c_device(struct gc0309_info *info)
{
    struct i2c_board_info board_info;

    memset(&board_info, 0, sizeof(struct i2c_board_info));
    board_info.addr = info->i2c_dev->addr;
    strlcpy(board_info.type, "gc0309", I2C_NAME_SIZE);

    info->i2c_dev = i2c_new_device(info->i2c_dev->adapter, &board_info);

    if (info->i2c_dev == NULL) {
        cam_err("can't add i2c device at 0x%x\n", (unsigned int)board_info.addr);
        return -ENODEV;
    }

    return 0;
}

static int __init gc0309_init(void)
{
	int ret;
	struct v4l2_subdev *sd;
	struct gc0309_info *info;

	info = kzalloc(sizeof(struct gc0309_info), GFP_KERNEL);
	if (info == NULL)
		return -ENOMEM;

	info->i2c_dev = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	if (info->i2c_dev == NULL)
		return -ENOMEM;

	/* get CAMERA clock */
	info->camera = clk_get(&info->i2c_dev->dev, "camera");
	if (IS_ERR(info->camera)) {
		cam_err("failed to acquire camera divider\n");
		ret = -ENODEV;
		goto exit;
	}

	clk_set_rate(info->camera, GC0309_SENSOR_CLOCK);
	clk_enable(info->camera);

	/* request gpio */
	gpio_request(GPIO_CAM_PWDN_B, "cam_pwdn_b");
	gpio_request(GPIO_CAM_PWDN_F, "cam_pwdn_f");
	gpio_request(GPIO_CAM_RST_F, "cam_rst_f");

	/* firstly poweron */
	gc0309_gpio_preset();
	gc0309_poweron();
	gc0309_gpio_reset();

	info->i2c_dev->addr = 0x21;
	if(gc0309_lookfor_bus(info)) goto exit_power;

	if(gc0309_register_i2c_device(info)) goto exit_power;

	/* after detection finished, poweroff */
	gc0309_standby_mode();
	gc0309_poweroff();

	sd = &info->sd;
	info->i2c_dev->driver = &gc0309_driver;
	v4l2_i2c_subdev_init(sd, info->i2c_dev, &gc0309_ops);

	ret = i2c_add_driver(&gc0309_driver);
	if (ret) {
		cam_err("gc0309_dev camera module gc0309 i2c driver add failed\n");
		return ret;
	}

	return 0;

exit_power:
	gc0309_standby_mode();
	gc0309_poweroff();
	clk_disable(info->camera);
	clk_put(info->camera);
exit:
	kfree(info->i2c_dev);
	kfree(info);
	return -EFAULT;
}

static void __exit gc0309_exit(void)
{
	i2c_del_driver(&gc0309_driver);
}

module_param(i2c_bus, int , 0);
MODULE_PARM_DESC(i2c_bus, "gc0309 i2c bus number");
剛開始寫的時候只管實現這個功能,沒有注意優化,感覺很多地方沒考慮周全,所以花時間優化了一下,下面就是優化後的ts代碼:

static int ft5x0x_add_i2c_device(void)
{
	int ret;
	int reg_value;
	struct i2c_board_info info;
	struct i2c_adapter *adapter;

	adapter = i2c_get_adapter(i2c_bus);
	if (!adapter) {
		atxxtp_err("can't get i2c adapter %d\n", i2c_bus);
		return -ENODEV;
	}

	memset(&info, 0, sizeof(struct i2c_board_info));
    info.addr = i2c_addr;
    strlcpy(info.type, FT5X0X_NAME, I2C_NAME_SIZE);

    this_client = i2c_new_device(adapter, &info);
	i2c_put_adapter(adapter);
    if (!this_client) {
        atxxtp_err("can't add i2c device at 0x%x\n", i2c_addr);
        return -ENODEV;
    }

	reg_value = ft5x0x_read_fw_ver();  //讀取ts的版本號  以此判斷設備是否存在
	if(reg_value < 0 ) {
		atxxtp_err("can't read ft5x06 firmware version\n");
		ret = -ENODEV;
		goto exit;
	}

	ret = i2c_add_driver(&ft5x0x_ts_driver);
	if (ret) {
		atxxtp_err("can't addd ft5x0x_ts i2c driver\n");
		goto exit;
	}

    return 0;

exit:
	i2c_unregister_device(this_client);
	return ret;
}

static int __init ft5x0x_ts_init(void)
{
	int ret;
	
	ret = gpio_request(GPIO_TP_RESETn, FT5X0X_NAME);
	if (ret != 0) {
		atxxtp_err( "can't request GPIO %d!\n", GPIO_TP_RESETn);
		return -EFAULT;
	}

	init_reset_tp();
	mdelay(100);
	
	if(ft5x0x_add_i2c_device())
		goto exit;	

	return 0;

exit:
	gpio_free(GPIO_TP_RESETn);
	return -EFAULT;
}

static void __exit ft5x0x_ts_exit(void)
{
	i2c_del_driver(&ft5x0x_ts_driver);
	i2c_unregister_device(this_client);
}

module_param(i2c_bus, int, 0);
MODULE_PARM_DESC(i2c_bus, "ft5x0x_ts i2c bus number");

module_param(i2c_addr, ushort, 0);
MODULE_PARM_DESC(i2c_addr, "ft5x0x_ts i2c address");

還好之前看過i2c的核心代碼並且總結了一下,不然還真得花些時間才能完成這個。

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