MENU

[转,备份]Qualcomm sdm845 eeprom驱动

March 17, 2020 • 转载自Randy Zhang,ARM World

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