mstar 平台内核i2c总线介绍

所有嵌入式平台都会用i2c 来挂载设备,mstar 平台一般会配置两组i2c,但是如果一路i2c 总线上面挂载设备太多或者设备地址出现冲突时,就需要添加新的总线。

硬件连接

  1. i2c0连接如下

i2c0也即给DCDC使用的一组总线,主要是调整cpu 核心电压,该总线不允许用户挂载其它设备
mstar 平台内核i2c总线介绍_第1张图片
对应的clk,data 是芯片pin脚是E6,F6

  1. i2c1 的连接如下

mstar 平台内核i2c总线介绍_第2张图片
对应的clk,data 是芯片pin脚是AE11,AD11

  1. i2c2 的连接如下

mstar 平台内核i2c总线介绍_第3张图片
对应的clk,data 是芯片pin脚是AD16,AC16

总线配置

通过code\vendor\mstar\supernova\projects\board\m7221\ChipInfo\MSD96BUXM8.h文件可以查到芯片pin 脚在代码里面的ID 值
mstar 平台内核i2c总线介绍_第4张图片
mstar 平台内核i2c总线介绍_第5张图片
mstar 平台内核i2c总线介绍_第6张图片
将这些值配置到内核dts 中
{PAD_DDCR_CK, PAD_DDCR_DA}
{PAD_TGPIO0 , PAD_TGPIO1}
{PAD_GPIO19 , PAD_GPIO20,100}
如下:

    i2c0: i2c@0 {
     
        compatible = "mstar,swi2c";
        bus-index = <0>;
        sda-gpio = <PAD_DDCR_DA>;
        scl-gpio = <PAD_DDCR_CK>;
        speed-khz = <100>;
        def-delay = <100>;
        retries = <5>;
        status = "okay";
        iic-mode = <1>;
        hw-port = <0>;
        pad-mux = <1>;
    };
    i2c1: i2c@1 {
     
        compatible = "mstar,swi2c";
        bus-index = <1>;
        sda-gpio = <PAD_TGPIO1>;
        scl-gpio = <PAD_TGPIO0>;
        speed-khz = <100>;
        def-delay = <100>;
        retries = <5>;
        status = "okay";
        iic-mode = <0>;
        hw-port = <0>;
        pad-mux = <0>;
    };
    i2c2: i2c@2 {
     
        compatible = "mstar,swi2c";
        bus-index = <2>;
        sda-gpio = <PAD_GPIO20>;
        scl-gpio = <PAD_GPIO19>;
        speed-khz = <100>;
        def-delay = <100>;
        retries = <5>;
        status = "okay";
        iic-mode = <0>;
        hw-port = <0>;
        pad-mux = <0>;
    };

查看节点

更新dts 后,烧录内核,可以查看到如下节点:
在这里插入图片描述

I2C属性

of_node 下面的节点对应的就是dts 下的节点
mstar 平台内核i2c总线介绍_第7张图片

I2C 注册流程

mstar 平台内核i2c总线介绍_第8张图片
i2c 类似标准的平台设置注册到系统里

static struct platform_driver Mstar_iic_driver = {
     
	.probe 		= mstar_iic_drv_probe,
	.remove		= mstar_iic_drv_remove,
	.suspend		= mstar_iic_drv_suspend,
	.resume		= mstar_iic_drv_resume,

	.driver = {
     
#if defined(CONFIG_OF)
		.of_match_table = mstariic_of_device_ids,
#endif
		.name	= "Mstar-iic",
		.owner	= THIS_MODULE,
	}
};


static int __init mstar_iic_drv_init_module(void)
{
     
    int retval=0;
    printk("[kswi2c] %s\n",__FUNCTION__);
    retval = platform_driver_register(&Mstar_iic_driver);
    return retval;
}

注册完之后,探测函数通过compatible 信息匹配找到驱动。

static int mstar_iic_drv_probe(struct platform_device *pdev)
{
     
	int retval = 0;
	struct mstar_i2c_dev *i2c_dev;
	struct i2c_adapter *adap;
	struct device_node *dn;

	......

	/* parse OF i2c info */
	dn = pdev->dev.of_node;
	if (dn)
	{
     
		retval = mstar_swi2c_parse_dt(dn, i2c_dev);
		if (retval < 0)
		{
     
			IIC_PRINT("[ki2c] unable to parse device tree\n");
			goto err_init_swi2c;
		}
	}

    ......

	i2c_dev->pdev = pdev;
	platform_set_drvdata(pdev, i2c_dev);

	/* init swi2c MDrv backend */
    if (i2c_dev->I2cAttr.iic_mode == MSTAR_SWIIC)
    {
     
		mstar_swi2c_init(i2c_dev);
    }
    else
    {
     
        mstar_hwi2c_init(i2c_dev);
    }

	/* register i2c adapter and bus algorithm */
	adap = &i2c_dev->adapter;
	adap->owner = THIS_MODULE;
	adap->class = I2C_CLASS_HWMON;
	strlcpy(adap->name, "Mstar I2C adapter", sizeof(adap->name));
	adap->dev.parent = &pdev->dev;
    adap->algo = &mstar_i2c_algo;
	adap->dev.of_node = pdev->dev.of_node;
	adap->retries = i2c_dev->I2cAttr.retries;
	adap->timeout = 2 * HZ;
	adap->nr = i2c_dev->I2cAttr.bus_index;

	i2c_set_adapdata(adap, i2c_dev);

	retval = i2c_add_adapter(adap);
    if (retval)
    {
     
	    dev_err(&pdev->dev, "failed to add i2c adapter");
	    goto err_gpio;
	}

	/* register i2c bus to dt */
 #if defined (CONFIG_OF)
	of_i2c_register_devices(adap);
#endif

	/* all set, now init the ioctl interface */
	mod_iic_init();
	return 0;

err_gpio:
	gpio_free(i2c_dev->I2cAttr.scl_pad);
	gpio_free(i2c_dev->I2cAttr.sda_pad);
err_init_swi2c:
	return retval;
}

解析完i2c 属性,开始添加i2c adapter(i2c-core.c)

int i2c_add_adapter(struct i2c_adapter *adapter)
{
     
	struct device *dev = &adapter->dev;
	int id;

	if (dev->of_node) {
     
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
     
			adapter->nr = id;
			return __i2c_add_numbered_adapter(adapter);
		}
	}

	mutex_lock(&core_lock);
	id = idr_alloc(&i2c_adapter_idr, adapter,
		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id;

	adapter->nr = id;

	return i2c_register_adapter(adapter);
}

注册i2c adapter 设置i2c 名称,创建设备节点等。

static int i2c_register_adapter(struct i2c_adapter *adap)
{
     
	int res = -EINVAL;

	/* Can't register until after driver model init */
	if (WARN_ON(!is_registered)) {
     
		res = -EAGAIN;
		goto out_list;
	}

	/* Sanity checks */
	if (WARN(!adap->name[0], "i2c adapter has no name"))
		goto out_list;

	if (!adap->algo) {
     
		pr_err("adapter '%s': no algo supplied!\n", adap->name);
		goto out_list;
	}

	if (!adap->lock_ops)
		adap->lock_ops = &i2c_adapter_lock_ops;

	rt_mutex_init(&adap->bus_lock);
	rt_mutex_init(&adap->mux_lock);
	mutex_init(&adap->userspace_clients_lock);
	INIT_LIST_HEAD(&adap->userspace_clients);

	/* Set default timeout to 1 second if not already set */
	if (adap->timeout == 0)
		adap->timeout = HZ;

	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);
	if (res) {
     
		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
		goto out_list;
	}

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

	pm_runtime_no_callbacks(&adap->dev);
	pm_suspend_ignore_children(&adap->dev, true);
	pm_runtime_enable(&adap->dev);

#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif

	i2c_init_recovery(adap);

	/* create pre-declared device nodes */
	of_i2c_register_devices(adap);
	i2c_acpi_register_devices(adap);
	i2c_acpi_install_space_handler(adap);

	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	mutex_lock(&core_lock);
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);

	return 0;

out_list:
	mutex_lock(&core_lock);
	idr_remove(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);
	return res;
}

i2c 编程

例如: 挂在i2c-2 上面的设备(地址为0xAC)

#define RTL5450_MASTER_DEV ("/dev/i2c-2")
#define RTL5450_IIC_ADDR   (0xAC >> 1)

打开i2c

int nFd = open(RTL5450_MASTER_DEV, O_RDWR);
if(nFd < 0)
{
     
    printf("[%s][%d] open %s failed !\n", __FUNCTION__,__LINE__,RTL5450_MASTER_DEV);
        return FALSE;
}
  1. 写多个字节
static MAPI_BOOL sendCmdToI2cDev(int nDevFd, MAPI_U8 u8Cmd[], MAPI_U8 u8Size)
{
     
    int nRet = -1;
    struct i2c_msg msg;
    struct i2c_rdwr_ioctl_data ioctl_data = {
     0};

    msg.addr          = RTL5450_IIC_ADDR;
    msg.flags         = I2C_SMBUS_WRITE; /* write */
    msg.len           = u8Size;
    msg.buf           = u8Cmd;

    ioctl_data.msgs   = &msg;
    ioctl_data.nmsgs  = 1;

    nRet = ioctl(nDevFd, I2C_RDWR, &ioctl_data);
    if( nRet < 0)
    {
     
        printf("[%s][%d] ioctl rtl5450 i2c reg[0x%02x] failed! nRet: = %d, errno: %d, error: %s. \n", \
            __FUNCTION__,__LINE__,u8Cmd[0],nRet, errno,strerror(errno));
        return MAPI_FALSE;
    }

    return MAPI_TRUE;
}

nDevFd为文件"/dev/i2c-2"的描述符,I2C_SMBUS_WRITE 为0,msg.len 为msg.buf 缓存区的大小。msg.buf 缓存区内容的格式: reg/cmd + len + data …, 写指令时,第一个一般为寄存器地址(某些地方称之为操作码),接着数据长度,再接着为数据。RTL5450_IIC_ADDR 要右移一位取前七位,传给内核后,底层会左移一位再与msg.flag 取与。

  1. 写单个字节
    u8Size 为2 ,u8Cmd[0] 为操作码(寄存器),
    u8Cmd[1] 为data
  2. 读多个字节
    读i2c 其实有两个操作,传递两个msg 给i2c 设备,msg[0]先将需要读取的寄存器地址传递给内核,msg[0].flag 为0(写); msg[1].flag = I2C_M_RD(也即1),msg[1].buf,msg[1].len分别为缓存区的地址 和大小。 master 端收到之后,通过msg[1] 的buf 返回数据。
static MAPI_BOOL readI2cDataFromI2cDev(int nDevFd, MAPI_U8 u8Cmd, MAPI_U8 *pu8FeedBuf, MAPI_U8 u8FeedSize)
{
     
    struct i2c_msg msg[2];
    struct i2c_rdwr_ioctl_data data_ioctl;
    MAPI_U8 ucAddr = u8Cmd;
    msg[0].addr = RTL5450_IIC_ADDR;
    msg[0].flags = I2C_SMBUS_WRITE;
    msg[0].len = 1;
    msg[0].buf = &ucAddr; // reg
    msg[1].addr = RTL5450_IIC_ADDR;
    msg[1].flags = I2C_M_RD;
    msg[1].len = u8FeedSize;
    msg[1].buf = pu8FeedBuf;

    data_ioctl.msgs = msg;
    data_ioctl.nmsgs = 2;

    if(ioctl(nDevFd, I2C_RDWR, &data_ioctl) < 0)
    {
     
        printf("[%s][%d] read back value failed! \n", __FUNCTION__,__LINE__);
        return MAPI_FALSE;
    }

    return MAPI_TRUE;
}
  1. 读单个字节
    同上,将缓存区减小为1即可。

  2. master 端返回值获取
    我们再调试i2c 设备时,使用的smbus 协议。碰到slave 发送指令给master端后,等待master 端的返回值,但是并不是通过某个寄存器返回的。

static MAPI_BOOL recvFeedbackValFromI2cDev(int nDevFd, MAPI_U8 *pu8FeedBuf)
{
     
    struct i2c_msg msg;
    struct i2c_rdwr_ioctl_data data_ioctl;
    msg.addr = RTL5450_IIC_ADDR;
    msg.flags = I2C_SMBUS_READ;
    msg.len = 1;
    msg.buf = pu8FeedBuf;

    data_ioctl.msgs = &msg;
    data_ioctl.nmsgs = 1;

    if(ioctl(nDevFd, I2C_RDWR, &data_ioctl) < 0)
    {
     
        printf("[%s][%d] read back value failed! \n", __FUNCTION__,__LINE__);
        return MAPI_FALSE;
    }
    return MAPI_TRUE;
}

此时slave 只往master 端下发了一个msg,且flag 为I2C_SMBUS_READ 也即1。 master 端会通过该msg 将返回值通过msg.buf 带回给slave端。

你可能感兴趣的:(商显,c++,linux,嵌入式,linux)