相关文章:
二. 普通IO模拟i2c总线
三. 简单i2c设备驱动实例
平台: msm8916
OS:安卓5.1
usb4604在设备上作用是切换设备为主从模式。
设备树文件如下:
microchip@2d {
compatible = "microchip,usb4604"; //驱动根据这个来匹配,然后调用prob函数,调试可在prob增加打印
reg = <0x2d>;
usbid-gpios = <&msm_gpio 32 0x00>;
reset-gpios = <&msm_gpio 72 0x00>;
};
usb4604驱动文件:
生成sys/bus/i2c/devices/0-002d/host节点,可以通过echo 0 > sys/bus/i2c/devices/0-002d/host 或者echo 1 > sys/bus/i2c/devices/0-002d/host 来切换主从,即向usb4604对应的寄存器写入对应的值。
/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/i2c.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/usb.h>
struct usb4604_chip {
struct i2c_client *client;
bool is_host;
int usbid_gpio;
int reset_gpio;
};
static int usb4604_i2c_write(struct usb4604_chip *chip,
u8 *buf, u8 len)
{
struct i2c_msg msgs =
{
.addr = chip->client->addr,
.flags = chip->client->flags,
.len = len,
.buf = buf,
};
return i2c_transfer(chip->client->adapter, &msgs, 1);
}
static int usb4604_switch(struct usb4604_chip *chip)
{
int ret, i;
u8 data[8];
if (chip->reset_gpio) {
ret = gpio_direction_output(chip->reset_gpio, 0);
if (ret) {
pr_err("%s: gpio %d output failed. ret = %d\n",
__func__, chip->is_host, ret);
} else {
mdelay(10);
ret = gpio_direction_output(chip->reset_gpio, 1);
if (ret) {
pr_err("%s: gpio %d output failed. ret = %d\n",
__func__, chip->is_host, ret);
}
else
mdelay(10);
}
}
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x05;
data[3] = 0x00;
data[4] = 0x01;
data[5] = 0x31;
data[6] = 0x8e;
data[7] = chip->is_host ? 0x03 : 0x02;//BIT(1)mode BIT(0)enable
for(i = 0; i < 30; i++) {
ret = usb4604_i2c_write(chip, data, 8);
if(ret > 0)
break;
pr_err("###############mdelay %d\n", i);
mdelay(10);
}
if(ret < 0)
return ret;
data[0] = 0x99;
data[1] = 0x37;
data[2] = 0x00;
ret = usb4604_i2c_write(chip, data, 3);
data[0] = 0xaa;
data[1] = 0x55;
data[2] = 0x00;
ret = usb4604_i2c_write(chip, data, 3);
pr_err("@@@@usb4604_switch %d\n", ret);
if(ret > 0) {
ret = gpio_direction_output(chip->usbid_gpio, chip->is_host);
if (ret) {
pr_err("%s: gpio %d output failed. ret = %d\n",
__func__, chip->is_host, ret);
}
}
else
pr_err("@@@@usb4604_switch I2C ERROR\n");
return ret;
}
static ssize_t host_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb4604_chip *chip = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", chip->is_host);
}
static ssize_t host_store(struct device *dev,
struct device_attribute *attr,
const char *buff, size_t size)
{
struct usb4604_chip *chip = dev_get_drvdata(dev);
long host;
int ret;
if (kstrtol(buff, 10, (long *)&host)) {
pr_err("can not recognize\n");
return size;
}
if (!!host != chip->is_host) {
chip->is_host = !!host;
ret = usb4604_switch(chip);
pr_info("@@@@host_store %d, ret = %d\n", chip->is_host, ret);
} else {
pr_info("the same as old %d", chip->is_host);
}
return size;
}
static ssize_t reset_store(struct device *dev,
struct device_attribute *attr,
const char *buff, size_t size)
{
struct usb4604_chip *chip = dev_get_drvdata(dev);
long reset;
int ret;
if(kstrtol(buff, 10, (long *)&reset)) {
pr_err("can not recognize\n");
return size;
}
if(chip->reset_gpio) {
ret = gpio_direction_output(chip->reset_gpio, !!reset);
if (ret) {
pr_err("%s: gpio %d output failed. ret = %d\n",
__func__, chip->reset_gpio, ret);
}
}
pr_info("@@@@host_store %d, ret = %d\n", chip->is_host, ret);
return size;
}
static DEVICE_ATTR(host, S_IRUGO | S_IWUSR, host_show, host_store);
static DEVICE_ATTR(reset, S_IWUSR,NULL, reset_store);
static struct device_attribute *usb4604_attributes[] = {
&dev_attr_host,
&dev_attr_reset,
NULL
};
static int usb4604_parse_dt(struct device *dev,
struct usb4604_chip *chip)
{
struct device_node *np = dev->of_node;
int ret = 0;
/* usbid gpio */
ret = of_get_named_gpio_flags(np, "usbid-gpios", 0, NULL);
if(ret < 0) {
dev_err(dev, "Unable to read usbid gpio. ret = %d\n", ret);
return ret;
}
chip->usbid_gpio = ret;
if (!gpio_is_valid(chip->usbid_gpio)) {
dev_err(dev,
"invalid power pin: %d\n", chip->usbid_gpio);
}
ret = gpio_request(chip->usbid_gpio, "usbid-gpios");
if (ret < 0) {
dev_err(dev,
"%s: gpio %d request failed. ret = %d\n",
__func__, chip->usbid_gpio, ret);
return ret;
}
/* reset gpio */
ret = of_get_named_gpio_flags(np, "reset-gpios", 0, NULL);
if(ret < 0) {
dev_err(dev, "Unable to read reset gpio. ret = %d\n", ret);
return ret;
}
chip->reset_gpio = ret;
if (!gpio_is_valid(chip->reset_gpio)) {
dev_err(dev,
"invalid power pin: %d\n", chip->reset_gpio);
}
ret = gpio_request(chip->reset_gpio, "usb4604-reset-gpios");
if (ret < 0) {
dev_err(dev,
"%s: gpio %d request failed. ret = %d\n",
__func__, chip->reset_gpio, ret);
return ret;
}
return 0;
}
static int usb4604_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct usb4604_chip *chip;
struct device_attribute *attr;
struct device_attribute **attrs = usb4604_attributes;
pr_info("usb4604_probe\n");
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) {
dev_err(&client->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
chip->client = client;
dev_set_drvdata(&client->dev, chip);
if (client->dev.of_node) {
ret = usb4604_parse_dt(&client->dev, chip);
if (ret) {
dev_err(&client->dev,"%s: usb4604_parse_dt() err\n", __func__);
goto err_parse_dt;
}
} else {
dev_err(&client->dev, "No dts data\n");
goto err_parse_dt;
}
while ((attr = *attrs++)) {
ret = device_create_file(&client->dev, attr);
if (ret)
goto err_parse_dt;
}
// if(tca8418_usb4604_gpio_init() < 0)
// pr_info("usb4604 power error\n");
return 0;
err_parse_dt:
devm_kfree(&client->dev,chip);
return ret;
}
static int usb4604_remove(struct i2c_client *client)
{
return 0;
}
static struct of_device_id usb4604_match_table[] = {
{ .compatible = "microchip,usb4604",},
{ },
};
static const struct i2c_device_id usb4604_id[] = {
{"usb4604", 0},
{},
};
static struct i2c_driver usb4604_driver = {
.driver = {
.name = "usb4604",
.owner = THIS_MODULE,
.of_match_table = usb4604_match_table,
},
.probe = usb4604_probe,
.remove = usb4604_remove,
.id_table = usb4604_id,
};
module_i2c_driver(usb4604_driver);
MODULE_DESCRIPTION("usb4604");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("i2c:usb4604");
- 向从机发送信息:
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
int ret;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1) ? count : ret;
}
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
int ret;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1) ? count : ret;
}
- 向从机读取信息:
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
int ret;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg received), return #bytes received,
* else error code.
*/
return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_recv);
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
int ret;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg received), return #bytes received,
* else error code.
*/
return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_recv);
可参考文档:参考文档地址