所有嵌入式平台都会用i2c 来挂载设备,mstar 平台一般会配置两组i2c,但是如果一路i2c 总线上面挂载设备太多或者设备地址出现冲突时,就需要添加新的总线。
i2c0也即给DCDC使用的一组总线,主要是调整cpu 核心电压,该总线不允许用户挂载其它设备
对应的clk,data 是芯片pin脚是E6,F6
通过code\vendor\mstar\supernova\projects\board\m7221\ChipInfo\MSD96BUXM8.h文件可以查到芯片pin 脚在代码里面的ID 值
将这些值配置到内核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>;
};
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-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;
}
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 取与。
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即可。
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端。