sdm845平台移植eeprom驱动。
设备树修改
- 修改设备树文件:
sdm845-androido/ap/kernel/msm-4.9 / arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
&qupv3_se7_i2c {
status = "ok";
[email protected] {
compatible = "i2c,eeprom";
reg = <0x57>;
qcom,power-en= <&tlmm 87 0x00>;
qcom,tof-en= <&tlmm 25 0x00>;
qcom,sda-en= <&tlmm 93 0x00>;
qcom,scl-en= <&tlmm 94 0x00>;
};
};
def_config配置config宏
在以下两文件中分别添加以下两行配置。
-
sdm845-androido/ap/kernel/msm-4.9 / arch/arm64/configs/sdm845_defconfig
-
sdm845-androido/ap/kernel/msm-4.9 / arch/arm64/configs/sdm845-perf_defconfig
CONFIG_I2C_EEPROM=y
drivers目录修改kconfig/Makefile
# sdm845-androido/ap/kernel/msm-4.9 / drivers/i2c/Kconfig
config I2C_EEPROM
bool "I2C Algorithm debugging messages"
help
Say Y here if you want the I2C algorithm drivers to produce a bunch
of debug messages to the system log. Select this if you are having
a problem with I2C support and want to see more of what is going
on.
# sdm845-androido/ap/kernel/msm-4.9 / drivers/i2c/Makefile
obj-$(CONFIG_I2C_EEPROM) += fmiic-eeprom.o
设备驱动文件添加
drivers目录增加设备驱动源文件:drivers/i2c/fmiic-eeprom.c
/*
* Driver for the TI eeprom battery charger.
*
* Author: Mark A. Greer <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regulator/driver.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/power/bq24190_charger.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
//#define BQ24190_MANUFACTURER "Texas Instruments"
#define MAX_BUFFER_SIZE 8192
/*
* The FAULT register is latched by the eeprom (except for NTC_FAULT)
* so the first read after a fault returns the latched value and subsequent
* reads return the current value. In order to return the fault status
* to the user, have the interrupt handler save the reg's value and retrieve
* it in the appropriate health/status routine. Each routine has its own
* flag indicating whether it should use the value stored by the last run
* of the interrupt handler or do an actual reg read. That way each routine
* can report back whatever fault may have occured.
*/
struct eeprom_dev_info {
struct i2c_client *client;
struct device *dev;
char model_name[I2C_NAME_SIZE];
kernel_ulong_t model;
struct mutex f_reg_lock;
struct mutex read_mutex;
bool first_time;
u8 f_reg;
u8 watchdog;
struct regulator *vdd;
struct miscdevice eeprom_device;
u8 *kbuf;
size_t kbuflen;
unsigned int power_en;
unsigned int tof_en;
unsigned int sda_en;
unsigned int scl_en;
};
#if 0
#define I2C_RDWR 0x0707
#define LNR_OFFSET_XPWR_TBL_SIZE 48
#define LNR_OFFSET_VAL_TBL_SIZE (LNR_OFFSET_XPWR_TBL_SIZE+1)
#define INTRINSIC_TBL_SIZE 9
#define DISTORTION_TBL_SIZE 5
#define OCCENTERDEVIATION_TBL_SIZE (2)
#define RCVERTEXBRIGHTNESSRATIO_TBL_SIZE (4)
#define SN_SIZE (17)
typedef unsigned int uint32;
typedef int int32;
typedef unsigned long long uint64;
typedef unsigned short uint16;
typedef short int16;
typedef unsigned char uint8;
typedef signed char int8;
#pragma pack(1)
struct calDataStruct
{
uint16 unIsatgVer;
uint8 unCalVer;
uint8 unTalEnable;
uint16 unDepthRange;
uint16 unLevel;
uint16 unPulseCnt;
uint16 unLaserEna;
uint16 unLnrX0;
uint16 unLD0;
uint16 unLD1;
uint16 unLD2;
uint16 unSub0;
uint16 unSub1;
uint16 unSub2;
uint8 ucHtpIsaEna;
uint8 eIsaMode;
uint8 unLnrXpwr[LNR_OFFSET_XPWR_TBL_SIZE];
int16 unOffset[LNR_OFFSET_VAL_TBL_SIZE];
uint16 DepthSlopeGain;
double fDepthCnvGain;
int16 DepthOfstVLU;
uint16 DepthMax;
uint8 ucDepthMode;
uint8 ucGridVGASel;
uint8 ucM3WSel;
char cSN[SN_SIZE];
int8 cOCCenterDeviation[OCCENTERDEVIATION_TBL_SIZE]; //(x, y) pixel
float fRCVertexBrightnessRatio[RCVERTEXBRIGHTNESSRATIO_TBL_SIZE]; //(leftTop, leftBottom, rightTop, rightBottom)
float fIntrinsic[INTRINSIC_TBL_SIZE];
double dDistortionCoeff[DISTORTION_TBL_SIZE];
int8 checksum;
};
#endif
#if 1
static int bq24190_read(const struct i2c_client *client, char *buf, int count,loff_t *offset)
{
int ret;
u8 bufaddr[2];
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg[2] = {
{
.len = 2,
.addr = client->addr,
.flags = 0,
.buf = bufaddr,
}, {
.addr = client->addr,
.flags = 1,
.len = count,
.buf = buf,
}
};
//bufaddr[0] = 0x00;
bufaddr[0] = ((*offset) >>8) & 0xff;
//bufaddr[1] = 0x00;
bufaddr[1] = ((*offset)) & 0xff;
ret = i2c_transfer(adap, msg, 2);
if (ret >0) {
return count;
} else {
return ret;
}
//return (ret == 1) ? count : ret;
}
#if 0
static int bq24190_write(const struct i2c_client *client, char *val, int count)
{
int ret;
u8 buf[400];
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg[1] = {
{
.addr = client->addr,
.flags = 0,
.len = 2 + count,
.buf = buf,
}
};
buf[0] = 0x00;
buf[1]= 0x00;
memcpy(&buf[2], val, count);
ret = i2c_transfer(adap, msg, 1);
if (ret == 1) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
#endif
static int eeprom_open(struct inode *inode, struct file *filp)
{
int ret = 0;
struct eeprom_dev_info *eeprom_dev = container_of(filp->private_data,
struct eeprom_dev_info, eeprom_device);
filp->private_data = eeprom_dev;
dev_dbg(&eeprom_dev->client->dev,
"%s: %d,%d\n", __func__, imajor(inode), iminor(inode));
return ret;
}
#endif
#ifdef CONFIG_OF
static int eeprom_setup_dt(struct eeprom_dev_info *bdi)
{
bdi->power_en = of_get_named_gpio(bdi->dev->of_node, "qcom,power-en", 0);
printk(KERN_EMERG "eeprom----power_en=%d\n",bdi->power_en);
bdi->tof_en = of_get_named_gpio(bdi->dev->of_node, "qcom,tof-en", 0);
printk(KERN_EMERG "eeprom----power_en=%d\n",bdi->tof_en);
bdi->sda_en = of_get_named_gpio(bdi->dev->of_node, "qcom,sda-en", 0);
printk(KERN_EMERG "eeprom----sda_en=%d\n",bdi->sda_en);
bdi->scl_en = of_get_named_gpio(bdi->dev->of_node, "qcom,scl-en", 0);
printk(KERN_EMERG "eeprom----scl_en=%d\n",bdi->scl_en);
return 0;
}
#else
static int eeprom_setup_dt(struct eeprom_dev_info *bdi)
{
return -1;
}
#endif
static int eeprom_setup_pdata(struct eeprom_dev_info *bdi,
struct bq24190_platform_data *pdata)
{
return 0;
}
static ssize_t eeprom_read(struct file *filp, char __user *buf,
size_t count, loff_t *offset)
{
struct eeprom_dev_info *eeprom_dev = filp->private_data;
unsigned char *tmp = NULL;
int ret,i;
//long long int j;
if (!eeprom_dev) {
ret = -ENODEV;
goto out;
}
ret = gpio_request(eeprom_dev->power_en, "eeprom_poweren_gpio");
ret = gpio_direction_output(eeprom_dev->power_en, 1);
if (ret < 0) {
goto err;
}
ret = gpio_request(eeprom_dev->tof_en, "eeprom_tof_gpio");
ret = gpio_direction_output(eeprom_dev->tof_en, 1);
if (ret < 0) {
goto err;
}
ret = gpio_request(eeprom_dev->power_en, "eeprom_sda_gpio");
ret = gpio_direction_output(eeprom_dev->sda_en, 1);
if (ret < 0) {
goto err;
}
ret = gpio_request(eeprom_dev->power_en, "eeprom_scl_gpio");
ret = gpio_direction_output(eeprom_dev->scl_en, 1);
if (ret < 0) {
goto err;
}
printk(KERN_EMERG "---------offset=%lld\n",(*offset));
dev_dbg(&eeprom_dev->client->dev, "%s : reading %zu bytes.\n",
__func__, count);
mutex_lock(&eeprom_dev->read_mutex);
if (count > MAX_BUFFER_SIZE)
count = MAX_BUFFER_SIZE;
tmp = eeprom_dev->kbuf;
if (!tmp) {
dev_err(&eeprom_dev->client->dev,
"%s: device doesn't exist anymore\n", __func__);
ret = -ENODEV;
goto err;
}
memset(tmp, 0x00, count);
/* Read data */
ret = bq24190_read(eeprom_dev->client, tmp, count,offset);
if (ret < 0) {
dev_err(&eeprom_dev->client->dev,
"%s: i2c_master_recv returned %d\n", __func__, ret);
goto err;
}
for (i=0;i<(count-1);i++)
//printk(KERN_EMERG "bufddata---buf[%d]=%x\n",i,*(tmp+i));
//printk(KERN_EMERG "-------ret=%d,count=%ld\n",ret,count);
if (ret > count) {
dev_err(&eeprom_dev->client->dev,
"%s: received too many bytes from i2c (%d)\n",
__func__, ret);
ret = -EIO;
goto err;
}
if (copy_to_user(buf, tmp, ret)) {
dev_warn(&eeprom_dev->client->dev,
"%s : failed to copy to user space\n", __func__);
ret = -EFAULT;
//printk(KERN_EMERG "copy to user faile-------------\n");
goto err;
}
mutex_unlock(&eeprom_dev->read_mutex);
ret = gpio_direction_output(eeprom_dev->power_en, 0);
ret = gpio_direction_output(eeprom_dev->tof_en, 0);
ret = gpio_direction_output(eeprom_dev->sda_en, 0);
ret = gpio_direction_output(eeprom_dev->scl_en, 0);
gpio_free(eeprom_dev->power_en);
gpio_free(eeprom_dev->tof_en);
gpio_free(eeprom_dev->sda_en);
gpio_free(eeprom_dev->scl_en);
return ret;
out:
return ret;
err:
gpio_free(eeprom_dev->power_en);
gpio_free(eeprom_dev->tof_en);
gpio_free(eeprom_dev->sda_en);
gpio_free(eeprom_dev->scl_en);
mutex_unlock(&eeprom_dev->read_mutex);
return ret;
}
#if 0
static ssize_t eeprom_write(struct file *filp, const char __user *buf,
size_t count, loff_t *offset)
{
struct eeprom_dev_info *eeprom_dev = filp->private_data;
char *tmp = NULL;
int ret = 0;
if (!eeprom_dev) {
ret = -ENODEV;
goto out;
}
tmp = memdup_user(buf, count);
if (IS_ERR(tmp)) {
dev_err(&eeprom_dev->client->dev, "%s: memdup_user failed\n",
__func__);
ret = PTR_ERR(tmp);
goto out;
}
ret = bq24190_write(eeprom_dev->client, tmp, count);
if (ret != count) {
dev_err(&eeprom_dev->client->dev,
"%s: failed to write %d\n", __func__, ret);
ret = -EIO;
goto out_free;
}
usleep_range(1000, 1100);
out_free:
kfree(tmp);
out:
return ret;
}
#endif
static const struct file_operations eeprom_dev_fops = {
.owner = THIS_MODULE,
.llseek = default_llseek,
.read = eeprom_read,
.write = NULL,
.open = eeprom_open,
};
static int eeprom_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct device *dev = &client->dev;
struct bq24190_platform_data *pdata = client->dev.platform_data;
struct eeprom_dev_info *bdi;
int ret;
printk(KERN_EMERG "[%s:%d]: eeprom_probe !\n",__FUNCTION__,__LINE__);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
return -ENODEV;
}
bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
if (!bdi) {
dev_err(dev, "Can't alloc bdi struct\n");
return -ENOMEM;
}
bdi->client = client;
bdi->dev = dev;
bdi->model = id->driver_data;
bdi->kbuflen = MAX_BUFFER_SIZE;
bdi->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL);
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
mutex_init(&bdi->read_mutex);
bdi->first_time = true;
i2c_set_clientdata(client, bdi);
if (dev->of_node)
ret = eeprom_setup_dt(bdi);
else
ret = eeprom_setup_pdata(bdi, pdata);
bdi->eeprom_device.minor = MISC_DYNAMIC_MINOR;
bdi->eeprom_device.name = "i2c-eeprom";
bdi->eeprom_device.fops = &eeprom_dev_fops;
ret = misc_register(&bdi->eeprom_device);
if (ret) {
dev_err(&client->dev, "%s: misc_register failed\n", __func__);
goto err_misc_register;
}
printk(KERN_EMERG "[%s:%d]: eeprom_probe success !\n",__FUNCTION__,__LINE__);
return 0;
err_misc_register:
mutex_destroy(&bdi->read_mutex);
return ret;
}
static int eeprom_remove(struct i2c_client *client)
{
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int eeprom_pm_suspend(struct device *dev)
{
return 0;
}
static int eeprom_pm_resume(struct device *dev)
{
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(eeprom_pm_ops, eeprom_pm_suspend, eeprom_pm_resume);
/*
* Only support the eeprom right now. The bq24192, bq24192i, and bq24193
* are similar but not identical so the driver needs to be extended to
* support them.
*/
static const struct i2c_device_id eeprom_i2c_ids[] = {
{ "eeprom", 1 },
{ },
};
#ifdef CONFIG_OF
static const struct of_device_id eeprom_of_match[] = {
{ .compatible = "i2c,eeprom", },
{ },
};
MODULE_DEVICE_TABLE(of, eeprom_of_match);
#else
static const struct of_device_id eeprom_of_match[] = {
{ },
};
#endif
static struct i2c_driver eeprom_driver = {
.probe = eeprom_probe,
.remove = eeprom_remove,
.id_table = eeprom_i2c_ids,
.driver = {
.name = "eeprom-iic",
.owner = THIS_MODULE,
.pm = &eeprom_pm_ops,
.of_match_table = of_match_ptr(eeprom_of_match),
},
};
module_i2c_driver(eeprom_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("randy zhang");
MODULE_ALIAS("i2c:eeprom-iic");
MODULE_DESCRIPTION("eeprom-iic");
init.rc中修改设备权限
chmod 0666 /dev/i2c-eeprom