(1)由于客户是给了个单片机的公版code ,需要在单片机的基础上修改成Android RK3566能用的linux内核的代码。
DTS 配置设备寄存器地址,7bit+1位的R/W 位, DTS 配置7bit。
DTS 配置:
fp9931: fp9931@18 {
status = "okay";
compatible = "ti,fp9931";
reg = <0x18>;
pinctrl-names = "default";
ebc_pmic_on-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>;
ebc_pwr_en-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>;
ebc_pwr_en_ts-gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;
ebc_pwr_pg-gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>;
};
(2)首先写出linux内核框架的fp9931_probe/fp9931_remove 两个大的框架函数。再里面完善
kernel:
/*
* Copyright (c) 2020 Rockchip Electronics Co. Ltd.
*
* Author: Zorro Liu <zorro.liu@rock-chips.com>
*/
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/version.h>
#include <linux/suspend.h>
#include <linux/soc/rockchip/rk_vendor_storage.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include "ebc_pmic.h"
/* After waking up from sleep, Papyrus
waits for VN to be discharged and all
voltage ref to startup before loading
the default EEPROM settings. So accessing
registers too early after WAKEUP could
cause the register to be overridden by
default values */
#define PAPYRUS_EEPROM_DELAY_MS 50
/* Papyrus WAKEUP pin must stay low for
a minimum time */
#define PAPYRUS_SLEEP_MINIMUM_MS 110
/* Temp sensor might take a little time to
settle eventhough the status bit in TMST1
state conversion is done - if read too early
0C will be returned instead of the right temp */
#define PAPYRUS_TEMP_READ_TIME_MS 10
/* Powerup sequence takes at least 24 ms - no need to poll too frequently */
#define HW_GET_STATE_INTERVAL_MS 24
#define SEQ_VDD(index) ((index % 4) << 6)
#define SEQ_VPOS(index) ((index % 4) << 4)
#define SEQ_VEE(index) ((index % 4) << 2)
#define SEQ_VNEG(index) ((index % 4) << 0)
/* power up seq delay time */
#define UDLY_3ms(index) (0x00 << ((index%4) * 2))
#define UDLY_6ms(index) (0x01 << ((index%4) * 2))
#define UDLY_9ms(index) (0x10 << ((index%4) * 2))
#define UDLY_12ms(index) (0x11 << ((index%4) * 2))
/* power down seq delay time */
#define DDLY_6ms(index) (0x00 << ((index%4) * 2))
#define DDLY_12ms(index) (0x01 << ((index%4) * 2))
#define DDLY_24ms(index) (0x10 << ((index%4) * 2))
#define DDLY_48ms(index) (0x11 << ((index%4) * 2))
#define NUMBER_PMIC_REGS 10
#define PAPYRUS_ADDR_TMST_VALUE 0x00
#define PAPYRUS_ADDR_ENABLE 0x01
#define PAPYRUS_ADDR_VADJ 0x02
#define PAPYRUS_ADDR_VCOM1_ADJUST 0x03
#define PAPYRUS_ADDR_VCOM2_ADJUST 0x04
#define PAPYRUS_ADDR_INT_ENABLE1 0x05
#define PAPYRUS_ADDR_INT_ENABLE2 0x06
#define PAPYRUS_ADDR_INT_STATUS1 0x07
#define PAPYRUS_ADDR_INT_STATUS2 0x08
#define PAPYRUS_ADDR_UPSEQ0 0x09
#define PAPYRUS_ADDR_UPSEQ1 0x0a
#define PAPYRUS_ADDR_DWNSEQ0 0x0b
#define PAPYRUS_ADDR_DWNSEQ1 0x0c
#define PAPYRUS_ADDR_TMST1 0x0d
#define PAPYRUS_ADDR_TMST2 0x0e
#define PAPYRUS_ADDR_PG_STATUS 0x0f
#define PAPYRUS_ADDR_REVID 0x10
// INT_ENABLE1
#define PAPYRUS_INT_ENABLE1_ACQC_EN 1
#define PAPYRUS_INT_ENABLE1_PRGC_EN 0
// INT_STATUS1
#define PAPYRUS_INT_STATUS1_ACQC 1
#define PAPYRUS_INT_STATUS1_PRGC 0
// VCOM2_ADJUST
#define PAPYRUS_VCOM2_ACQ 7
#define PAPYRUS_VCOM2_PROG 6
#define PAPYRUS_VCOM2_HIZ 5
#define PAPYRUS_MV_TO_VCOMREG(MV) ((MV) / 10)
#define V3P3_EN_MASK 0x20
#define PAPYRUS_V3P3OFF_DELAY_MS 20//100
struct papyrus_sess {
struct device *dev;
struct i2c_client *client;
uint8_t enable_reg_shadow;
uint8_t vadj;
uint8_t vcom1;
uint8_t vcom2;
uint8_t upseq0;
uint8_t upseq1;
uint8_t dwnseq0;
uint8_t dwnseq1;
int irq;
struct gpio_desc *int_pin;
struct gpio_desc *ebc_pmic_on;
struct gpio_desc *ebc_pwr_pg;
struct gpio_desc *ebc_pwr_en;
struct gpio_desc *ebc_pwr_en_ts;
struct mutex power_lock;
struct workqueue_struct *tmp_monitor_wq;
struct delayed_work tmp_delay_work;
};
struct papyrus_hw_state {
uint8_t tmst_value;
uint8_t int_status1;
uint8_t int_status2;
uint8_t pg_status;
};
static bool papyrus_need_reconfig = true;
/*register setting:*/
#define FP9931_TMST_VALUE 0x00
#define FP9931_VCOM_SETTING 0x01
#define FP9931_VP_VN_SETTING 0x02
#define FP9931_PWRON_DELAY 0x03
#define FP9931_CONTROL_REG1 0x0B
#define FP9931_CONTROL_REG2 0x0C
//for Temperature
#define TEMP_MAX 85
#define TEMP_MIN -10
//for VCOM setting
#define VCOM_VALUE_DEFAULT 80 //80 --1.5v
//for VP/VN setting
#define VP_VN_VALUE_MASK 0x3F
#define VP_VN_VALUE_MAX 0x29
#define VP_VN_VALUE_DEFAULT 0x28
//for Power ON Delay
#define PON_DELAY_TD1_MASK 0x03
#define PON_DELAY_TD2_MASK 0x0C
#define PON_DELAY_TD3_MASK 0x30
#define PON_DELAY_TD4_MASK 0xC0
#define PON_DELAY_TD1_INDEX 0
#define PON_DELAY_TD2_INDEX 1
#define PON_DELAY_TD3_INDEX 2
#define PON_DELAY_TD4_INDEX 3
#define DELAY_0MS 0
#define DELAY_1MS 1
#define DELAY_2MS 2
#define DELAY_4MS 3
//for Control REG1
#define CRTL_REG1_SS_TIME_MASK 0xC0
#define CRTL_REG1_V3P3_MASK 0x02
#define SS_TIME_3MS 0
#define SS_TIME_4MS 1
#define SS_TIME_5MS 2
#define SS_TIME_6MS 3
//for Control REG2
#define CRTL_REG2_FIX_RD_PTR_MASK 0x80
#define CRTL_REG2_VP_CP_MASK 0x0C
#define CRTL_REG2_VN_CP_MASK 0x03
#define CURR_LIMIT_3P5A 0
#define CURR_LIMIT_4P0A 1
#define CURR_LIMIT_4P5A 2
#define CURR_LIMIT_5P0A 3
struct fp9931_t {
uint8_t data[3];
int16_t temp;
};
static uint8_t vcom_default=VCOM_VALUE_DEFAULT;
extern void __maybe_unused fp9931_resume_init(struct papyrus_sess *sess);
static int papyrus_hw_setreg(struct papyrus_sess *sess, uint8_t regaddr, uint8_t val)
{
int stat;
uint8_t txbuf[2] = { regaddr, val };
struct i2c_msg msgs[] = {
{
.addr = sess->client->addr,
.flags = 0,
.len = 2,
.buf = txbuf,
}
};
stat = i2c_transfer(sess->client->adapter, msgs, ARRAY_SIZE(msgs));
if (stat < 0) {
dev_err(&sess->client->dev, "i2c send error: %d\n", stat);
} else if (stat != ARRAY_SIZE(msgs)) {
dev_err(&sess->client->dev, "i2c send N mismatch: %d\n", stat);
stat = -EIO;
} else {
stat = 0;
}
return stat;
}
static int papyrus_hw_getreg(struct papyrus_sess *sess, uint8_t regaddr, uint8_t *val)
{
int stat;
struct i2c_msg msgs[] = {
{
.addr = sess->client->addr,
.flags = 0,
.len = 1,
.buf = ®addr,
},
{
.addr = sess->client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
}
};
stat = i2c_transfer(sess->client->adapter, msgs, ARRAY_SIZE(msgs));
if (stat < 0) {
dev_err(&sess->client->dev, "i2c read error: %d\n", stat);
} else if (stat != ARRAY_SIZE(msgs)) {
dev_err(&sess->client->dev, "i2c read N mismatch: %d\n", stat);
stat = -EIO;
} else {
stat = 0;
}
return stat;
}
#if 0
static void papyrus_hw_get_int_state(struct papyrus_sess *sess, struct papyrus_hw_state *hwst)
{
int stat;
stat = papyrus_hw_getreg(sess, PAPYRUS_ADDR_INT_STATUS1, &hwst->int_status1);
if (stat)
dev_err(&sess->client->dev, "i2c error: %d\n", stat);
stat = papyrus_hw_getreg(sess, PAPYRUS_ADDR_INT_STATUS2, &hwst->int_status2);
if (stat)
dev_err(&sess->client->dev, "i2c error: %d\n", stat);
}
static bool papyrus_hw_power_ack(struct papyrus_sess *sess, int up)
{
struct papyrus_hw_state hwst;
int st;
int retries_left = 10;
if ((up & ~(1UL))) {
dev_err(&sess->client->dev, "invalid power flag %d.\n", up);
return false;
}
do {
papyrus_hw_getreg(sess, PAPYRUS_ADDR_PG_STATUS, &hwst.pg_status);
dev_dbg(&sess->client->dev, "hwst: tmst_val=%d, ist1=%02x, ist2=%02x, pg=%02x\n",
hwst.tmst_value, hwst.int_status1, hwst.int_status2, hwst.pg_status);
hwst.pg_status &= 0xfa;
if ((hwst.pg_status == 0xfa) && (up == 1)) {
st = 1;
} else if ((hwst.pg_status == 0x00) && (up == 0)) {
st = 0;
} else {
st = -1; /* not settled yet */
msleep(HW_GET_STATE_INTERVAL_MS);
}
retries_left--;
} while ((st == -1) && retries_left);
if (st == -1)
dev_err(&sess->client->dev, "power %s settle error (PG = %02x)\n", up ? "up" : "down", hwst.pg_status);
return (st == up);
}
#endif
static void papyrus_hw_send_powerup(struct papyrus_sess *sess)
{
#if 0
int stat = 0;
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VADJ, sess->vadj);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_UPSEQ0, sess->upseq0);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_UPSEQ1, sess->upseq1);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_DWNSEQ0, sess->dwnseq0);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_DWNSEQ1, sess->dwnseq1);
//stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VCOM1_ADJUST, sess->vcom1);
//stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VCOM2_ADJUST, sess->vcom2);
sess->enable_reg_shadow |= V3P3_EN_MASK;
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_ENABLE, sess->enable_reg_shadow);
usleep_range(2 * 1000, 3 * 1000);
sess->enable_reg_shadow = (0x80 | 0x30 | 0x0F);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_ENABLE, sess->enable_reg_shadow);
if (stat)
dev_err(&sess->client->dev, "i2c error: %d\n", stat);
#endif
if (!IS_ERR_OR_NULL(sess->ebc_pmic_on))
gpiod_direction_output(sess->ebc_pmic_on, 1);
printk("fp9931 papyrus_hw_send_powerup\n");
return;
}
static void papyrus_hw_send_powerdown(struct papyrus_sess *sess)
{
#if 0
int stat = 0;
sess->enable_reg_shadow = (0x40 | 0x20 | 0x0F);
stat = papyrus_hw_setreg(sess, PAPYRUS_ADDR_ENABLE, sess->enable_reg_shadow);
usleep_range(2 * 1000, 3 * 1000);
sess->enable_reg_shadow &= ~V3P3_EN_MASK;
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_ENABLE, sess->enable_reg_shadow);
if (stat)
dev_err(&sess->client->dev, "i2c error: %d\n", stat);
#endif
printk("fp9931 papyrus_hw_send_powerdown\n");
return;
}
#if 0
static irqreturn_t papyrus_irq(int irq, void *dev_id)
{
struct papyrus_sess *sess = dev_id;
struct papyrus_hw_state hwst;
papyrus_hw_get_int_state(sess, &hwst);
dev_info(&sess->client->dev, "%s: (INT1 = %02x, INT2 = %02x)\n", __func__,
hwst.int_status1, hwst.int_status2);
//reset pmic
if ((hwst.int_status2 & 0xfa) || (hwst.int_status1 & 0x04)) {
if (sess->enable_reg_shadow | V3P3_EN_MASK)
papyrus_hw_setreg(sess, PAPYRUS_ADDR_ENABLE, sess->enable_reg_shadow);
}
return IRQ_HANDLED;
}
static int papyrus_hw_get_revid(struct papyrus_sess *sess)
{
int stat;
uint8_t revid;
stat = papyrus_hw_getreg(sess, PAPYRUS_ADDR_REVID, &revid);
if (stat) {
dev_err(&sess->client->dev, "i2c error: %d\n", stat);
return stat;
} else {
return revid;
}
}
#endif
static void papyrus_hw_arg_init(struct papyrus_sess *sess)
{
/*
sess->vadj = 0x03;
sess->upseq0 = SEQ_VEE(0) | SEQ_VNEG(1) | SEQ_VPOS(2) | SEQ_VDD(3);
sess->upseq1 = UDLY_3ms(0) | UDLY_3ms(1) | UDLY_3ms(2) | UDLY_3ms(3);
sess->dwnseq0 = SEQ_VDD(0) | SEQ_VPOS(1) | SEQ_VNEG(2) | SEQ_VEE(3);
sess->dwnseq1 = DDLY_6ms(0) | DDLY_6ms(1) | DDLY_6ms(2) | DDLY_6ms(3);
sess->vcom1 = (PAPYRUS_MV_TO_VCOMREG(1560) & 0x00FF);
sess->vcom2 = ((PAPYRUS_MV_TO_VCOMREG(1560) & 0x0100) >> 8);
*/
printk("fp9931 papyrus_hw_arg_init\n");
}
#if 0
static int papyrus_hw_init(struct papyrus_sess *sess)
{
int stat = 0;
if (!IS_ERR_OR_NULL(sess->ebc_pmic_on)) {
gpiod_direction_output(sess->ebc_pmic_on, 1);
usleep_range(2 * 1000, 3 * 1000);
}
gpiod_direction_output(sess->ebc_pwr_en, 0);
/* wait to reset papyrus */
msleep(PAPYRUS_SLEEP_MINIMUM_MS);
gpiod_direction_output(sess->ebc_pwr_en, 1);
/* power up pin no need to control, use i2c control */
if (!IS_ERR_OR_NULL(sess->ebc_pwr_pg))
gpiod_direction_output(sess->ebc_pwr_pg, 0);
gpiod_direction_output(sess->ebc_pwr_en_ts, 1);
msleep(PAPYRUS_EEPROM_DELAY_MS);
stat = papyrus_hw_get_revid(sess);
if (stat < 0) {
dev_err(&sess->client->dev, "get id failed");
return stat;
}
dev_info(&sess->client->dev, "detected device with ID=%02x (TPS6518%dr%dp%d)\n",
stat, stat & 0xF, (stat & 0xC0) >> 6, (stat & 0x30) >> 4);
return 0;
}
static void papyrus_set_vcom_voltage(struct papyrus_sess *sess, int vcom_mv)
{
sess->vcom1 = (PAPYRUS_MV_TO_VCOMREG(vcom_mv) & 0x00FF);
sess->vcom2 = ((PAPYRUS_MV_TO_VCOMREG(vcom_mv) & 0x0100) >> 8);
}
#endif
static int papyrus_hw_read_temperature(struct ebc_pmic *pmic, int *t)
{
struct papyrus_sess *sess = (struct papyrus_sess *)pmic->drvpar;
int stat;
//uint8_t tb;
struct fp9931_t fp9931_info;
#if 0
int ntries = 50;
stat = papyrus_hw_setreg(sess, PAPYRUS_ADDR_TMST1, 0x80);
if (stat)
return stat;
do {
stat = papyrus_hw_getreg(sess, PAPYRUS_ADDR_TMST1, &tb);
} while (!stat && ntries-- && (((tb & 0x20) == 0) || (tb & 0x80)));
if (stat)
return stat;
msleep(PAPYRUS_TEMP_READ_TIME_MS);
stat = papyrus_hw_getreg(sess, PAPYRUS_ADDR_TMST_VALUE, &tb);
*t = (int)(int8_t)tb;
#endif
stat = papyrus_hw_getreg(sess,FP9931_TMST_VALUE,&fp9931_info.data[0]);
if(stat<0)
{
printk("fp9931 update temperature error\n");
return stat;
}
//convert ADC value to temperature (degree)
fp9931_info.temp = (fp9931_info.data[0] > 127) ? ((int)fp9931_info.data[0] - 256) : (int)fp9931_info.data[0];
if(fp9931_info.temp > TEMP_MAX)
fp9931_info.temp = TEMP_MAX;
if(fp9931_info.temp < TEMP_MIN)
fp9931_info.temp = TEMP_MIN;
printk("fp9931 update temperature success temp=%d\n",fp9931_info.temp);
fp9931_info.temp=25; // for test set 25 value
printk("fp9931 update temperature default temp=%d\n",fp9931_info.temp);
*t = (int)(int8_t)fp9931_info.temp;
return stat;
}
static void papyrus_hw_power_req(struct ebc_pmic *pmic, bool up)
{
struct papyrus_sess *sess = (struct papyrus_sess *)pmic->drvpar;
if (up)
mutex_lock(&sess->power_lock);
if (papyrus_need_reconfig) {
if (up) {
papyrus_hw_send_powerup(sess);
//papyrus_hw_power_ack(sess, up);
enable_irq(sess->irq);
} else {
disable_irq(sess->irq);
papyrus_hw_send_powerdown(sess);
//papyrus_hw_power_ack(sess, up);
}
papyrus_need_reconfig = false;
} else {
if (up) {
if (!IS_ERR_OR_NULL(sess->ebc_pmic_on))
gpiod_direction_output(sess->ebc_pmic_on, 1);
enable_irq(sess->irq);
} else {
disable_irq(sess->irq);
if (!IS_ERR_OR_NULL(sess->ebc_pmic_on))
gpiod_direction_output(sess->ebc_pmic_on, 0);
}
}
if (!up)
mutex_unlock(&sess->power_lock);
printk("fp9931 papyrus_hw_power_req up=%d papyrus_need_reconfig=%d\n",up,papyrus_need_reconfig);
return;
}
static int papyrus_hw_vcom_get(struct ebc_pmic *pmic)
{
struct papyrus_sess *sess = (struct papyrus_sess *)pmic->drvpar;
//uint8_t rev_val = 0;
int stat = 0;
uint8_t read_vcom_mv = 0;
int vcom_num=0;
#if 0
printk("fp9931 papyrus_hw_vcom_get \n");
mutex_lock(&sess->power_lock);
// VERIFICATION
gpiod_direction_output(sess->ebc_pwr_en, 0);
msleep(10);
gpiod_direction_output(sess->ebc_pwr_en, 1);
msleep(10);
read_vcom_mv = 0;
stat |= papyrus_hw_getreg(sess, PAPYRUS_ADDR_VCOM1_ADJUST, &rev_val);
read_vcom_mv += rev_val;
stat |= papyrus_hw_getreg(sess, PAPYRUS_ADDR_VCOM2_ADJUST, &rev_val);
read_vcom_mv += ((rev_val & 0x0001) << 8);
if (stat)
dev_err(&sess->client->dev, "papyrus: I2C error: %d\n", stat);
mutex_unlock(&sess->power_lock);
#endif
stat=papyrus_hw_getreg(sess,FP9931_VCOM_SETTING,&read_vcom_mv);
if(stat<0)
{
printk("fp9931 papyrus_hw_vcom_get error \n");
return stat;
}
// 5*read_vcom_mv*1000 /255;
vcom_num= (read_vcom_mv*1000)/51;
//vcom_num= (read_vcom_mv*1000)/50;
vcom_default=read_vcom_mv;
printk("fp9931 papyrus_hw_vcom_get success vcom_num=%d,read_vcom_mv=%d\n",vcom_num,read_vcom_mv);
return vcom_num;
}
static int papyrus_hw_vcom_set(struct ebc_pmic *pmic, int vcom_mv)
{
struct papyrus_sess *sess = (struct papyrus_sess *)pmic->drvpar;
#if 0
uint8_t rev_val = 0;
int stat = 0;
printk("fp9931 papyrus_hw_vcom_set \n");
mutex_lock(&sess->power_lock);
gpiod_direction_output(sess->ebc_pwr_en, 1);
msleep(10);
// Set vcom voltage
papyrus_set_vcom_voltage(sess, vcom_mv);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VCOM1_ADJUST, sess->vcom1);
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VCOM2_ADJUST, sess->vcom2);
// PROGRAMMING
sess->vcom2 |= 1 << PAPYRUS_VCOM2_PROG;
stat |= papyrus_hw_setreg(sess, PAPYRUS_ADDR_VCOM2_ADJUST, sess->vcom2);
rev_val = 0;
while (!(rev_val & (1 << PAPYRUS_INT_STATUS1_PRGC))) {
stat |= papyrus_hw_getreg(sess, PAPYRUS_ADDR_INT_STATUS1, &rev_val);
if (stat)
break;
msleep(50);
}
if (stat)
dev_err(&sess->client->dev, "papyrus: I2C error: %d\n", stat);
mutex_unlock(&sess->power_lock);
#endif
// 5/255 = 1 step;
int stat=0;
uint8_t vcom_num=0;
//vcom_mv = vcom_mv;//vcom_mv &0xff; //default 1.55v
vcom_num = (vcom_mv*51)/1000;
//vcom_num = (vcom_mv*50)/1000;
//stat=papyrus_hw_setreg(sess,FP9931_VCOM_SETTING,vcom_mv);
mutex_lock(&sess->power_lock);
gpiod_direction_output(sess->ebc_pmic_on, 1);
msleep(25);
stat=papyrus_hw_setreg(sess,FP9931_VCOM_SETTING,vcom_num);
if(stat<0)
{
printk("papyrus_hw_vcom_set fail");
}
msleep(10);
gpiod_direction_output(sess->ebc_pmic_on, 0);
mutex_unlock(&sess->power_lock);
printk("fp9931 papyrus_hw_vcom_set vcom_num=%d,vcom_mv=%d,stat=%d\n",vcom_num,vcom_mv,stat);
return 0;
}
static void papyrus_pm_sleep(struct ebc_pmic *pmic)
{
struct papyrus_sess *s = (struct papyrus_sess *)pmic->drvpar;
printk("fp9931 papyrus_pm_sleep \n");
cancel_delayed_work_sync(&s->tmp_delay_work);
mutex_lock(&s->power_lock);
gpiod_direction_output(s->ebc_pwr_en_ts, 0);
gpiod_direction_output(s->ebc_pwr_en, 0);
if (!IS_ERR_OR_NULL(s->ebc_pmic_on))
gpiod_direction_output(s->ebc_pmic_on, 0);
papyrus_need_reconfig = true;
mutex_unlock(&s->power_lock);
}
static void papyrus_pm_resume(struct ebc_pmic *pmic)
{
struct papyrus_sess *s = (struct papyrus_sess *)pmic->drvpar;
printk("fp9931 papyrus_pm_resume \n");
mutex_lock(&s->power_lock);
gpiod_direction_output(s->ebc_pwr_en, 1);
gpiod_direction_output(s->ebc_pwr_en_ts, 1);
usleep_range(3 * 1000, 4 * 1000);
/*
if (!IS_ERR_OR_NULL(s->ebc_pmic_on)) {
gpiod_direction_output(s->ebc_pmic_on, 1);
usleep_range(2 * 1000, 3 * 1000);
}
*/
mutex_unlock(&s->power_lock);
//trigger temperature measurement
//papyrus_hw_setreg(s, PAPYRUS_ADDR_TMST1, 0x80);
fp9931_resume_init(s);
queue_delayed_work(s->tmp_monitor_wq, &s->tmp_delay_work,
msecs_to_jiffies(10000));
}
static void papyrus_tmp_work(struct work_struct *work)
{
struct papyrus_sess *s =
container_of(work, struct papyrus_sess, tmp_delay_work.work);
printk("fp9931 papyrus_tmp_work \n");
//trigger temperature measurement
//papyrus_hw_setreg(s, PAPYRUS_ADDR_TMST1, 0x80);
queue_delayed_work(s->tmp_monitor_wq, &s->tmp_delay_work,
msecs_to_jiffies(10000));
}
static int fp9931_update_temperature(struct papyrus_sess *sess)
{
// if(HAL_I2C_Mem_Read(&hi2c2, FP9931_I2C_ADDR, fp9931_TMST_VALUE, 1, &fp9931_info.data[0], 1, 10) == HAL_I2C_ERROR_NONE)
// {
// //don't update temperature
// return err_msg_I2C;
// }
int stat;
struct fp9931_t fp9931_info;
stat = papyrus_hw_getreg(sess,FP9931_TMST_VALUE,&fp9931_info.data[0]);
if(stat<0)
{
printk("fp9931 update temperature error\n");
return stat;
}
//convert ADC value to temperature (degree)
fp9931_info.temp = (fp9931_info.data[0] > 127) ? ((int)fp9931_info.data[0] - 256) : (int)fp9931_info.data[0];
if(fp9931_info.temp > TEMP_MAX)
fp9931_info.temp = TEMP_MAX;
if(fp9931_info.temp < TEMP_MIN)
fp9931_info.temp = TEMP_MIN;
printk("fp9931 update temperature success \n");
return 0;
}
static int fp9931_set_vcom_adjust(struct papyrus_sess *sess,int value)
{
int stat;
stat=papyrus_hw_setreg(sess,FP9931_VCOM_SETTING,value);
if(stat<0)
{
printk("fp9931 set vcom adjust error \n");
return stat;
}
printk("fp9931 set vcom adjust success value=%d\n",value);
return 0;
}
static int fp9931_set_vpvn_adjust(struct papyrus_sess *sess,int value)
{
int stat;
uint8_t data;
stat=papyrus_hw_setreg(sess,FP9931_VP_VN_SETTING,value);
if (stat<0)
{
printk("fp9931 set vpvn adjust error \n");
return stat;
}
printk("fp9931 set vpvn adjust success\n");
stat = papyrus_hw_getreg(sess,FP9931_VP_VN_SETTING,&data);
{
printk("fp9931 get FP9931_VP_VN_SETTING 0 data=%x",data);
}
return 0;
}
static int fp9931_set_power_on_delay(struct papyrus_sess *sess,uint8_t index, uint8_t delay)
{
uint8_t time;
int stat;
//printk("fp9931 set power on delay\n");
// if(HAL_I2C_Mem_Read(&hi2c2, FP9931_I2C_ADDR, fp9931_PWRON_DELAY, 1, &time, 1, 10) == HAL_I2C_ERROR_NONE)
// {
// return err_msg_I2C;
// }
stat=papyrus_hw_getreg(sess,FP9931_PWRON_DELAY,&time);
if(stat<0)
{
printk("fp9931 set power on delay get reg error\n");
return stat;
}
if(index == PON_DELAY_TD1_INDEX)
{
time &= ~PON_DELAY_TD1_MASK;
time |= (delay & 0x03);
}
else if(index == PON_DELAY_TD2_INDEX)
{
time &= ~PON_DELAY_TD2_MASK;
time |= (delay & 0x03) << 2;
}
else if(index == PON_DELAY_TD3_INDEX)
{
time &= ~PON_DELAY_TD3_MASK;
time |= (delay & 0x03) << 4;
}
else if(index == PON_DELAY_TD4_INDEX)
{
time &= ~PON_DELAY_TD4_MASK;
time |= (delay & 0x03) << 6;
}
//if(HAL_I2C_Mem_Write(&hi2c2, FP9931_I2C_ADDR, fp9931_PWRON_DELAY, 1, &time, 1, 10) == HAL_I2C_ERROR_NONE)
//{
// return err_msg_I2C;
//}
stat=papyrus_hw_setreg(sess,FP9931_PWRON_DELAY,time);
if(stat<0)
{
printk("fp9931 set power on delay set reg error\n");
return stat;
}
printk("fp9931 set power on delay success\n");
return 0;
}
static int fp9931_set_soft_start_time(struct papyrus_sess *sess,int time)
{
uint8_t data;
int stat;
stat=papyrus_hw_getreg(sess,FP9931_CONTROL_REG1,&data);
if(stat<0)
{
return stat;
}
data &= ~CRTL_REG1_SS_TIME_MASK;
data |= (time & 0x03) << 6;
data |= 0x02;
//printk("fp9931 set FP9931_CONTROL_REG1 data=%x",data);
stat=papyrus_hw_setreg(sess,FP9931_CONTROL_REG1,data);
if(stat<0)
{
return stat;
}
printk("fp9931 set soft start time\n");
return 0;
}
static int fp9931_set_vp_curr_limit(struct papyrus_sess *sess,uint8_t curr)
{
uint8_t data;
int stat ;
//if(HAL_I2C_Mem_Read(&hi2c2, FP9931_I2C_ADDR, fp9931_CONTROL_REG2, 1, &data, 1, 10) == HAL_I2C_ERROR_NONE)
//{
// return err_msg_I2C;
//}
stat=papyrus_hw_getreg(sess,FP9931_CONTROL_REG2,&data);
if(stat<0)
{
return stat;
}
data &= ~CRTL_REG2_VP_CP_MASK;
data |= (curr & 0x03) << 2;
//if(HAL_I2C_Mem_Write(&hi2c2, FP9931_I2C_ADDR, fp9931_CONTROL_REG2, 1, &data, 1, 10) == HAL_I2C_ERROR_NONE)
//{
// return err_msg_I2C;
//}
stat=papyrus_hw_setreg(sess,FP9931_CONTROL_REG2,data);
//return err_msg_NONE;
if(stat<0)
{
return stat;
}
printk("fp9931 set vp curr limit success\n");
return 0;
}
static int fp9931_set_vn_curr_limit(struct papyrus_sess *sess,uint8_t curr)
{
uint8_t data;
int stat;
//if(HAL_I2C_Mem_Read(&hi2c2, FP9931_I2C_ADDR, fp9931_CONTROL_REG2, 1, &data, 1, 10) == HAL_I2C_ERROR_NONE)
//{
// return err_msg_I2C;
//}
stat=papyrus_hw_getreg(sess,FP9931_CONTROL_REG2,&data);
if(stat<0)
{
return stat;
}
data &= ~CRTL_REG2_VN_CP_MASK;
data |= (curr & 0x03);
//if(HAL_I2C_Mem_Write(&hi2c2, FP9931_I2C_ADDR, fp9931_CONTROL_REG2, 1, &data, 1, 10) == HAL_I2C_ERROR_NONE)
//{
// return err_msg_I2C;
//}
stat=papyrus_hw_setreg(sess,FP9931_CONTROL_REG2,data);
if(stat<0)
{
return stat;
}
//return err_msg_NONE;
printk("fp9931 set vn curr limit success\n");
return 0;
}
static void fp9931_init(struct papyrus_sess *sess)
{
mutex_lock(&sess->power_lock);
gpiod_direction_output(sess->ebc_pwr_en, 1);
msleep(250);
gpiod_direction_output(sess->ebc_pmic_on, 1);
msleep(250);
gpiod_direction_output(sess->ebc_pwr_en_ts, 1);
msleep(250);
gpiod_direction_output(sess->ebc_pwr_pg, 1);
msleep(250);
//update temperature
fp9931_update_temperature(sess);
//initial VCOM setting (by customer)
fp9931_set_vcom_adjust(sess,vcom_default);
//initial VP/VN setting (by customer)
fp9931_set_vpvn_adjust(sess,VP_VN_VALUE_DEFAULT);
//initial power on delay (by customer)
fp9931_set_power_on_delay(sess,PON_DELAY_TD1_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD2_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD3_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD4_INDEX, DELAY_0MS);
//PMIC soft start time setting (by customer)
fp9931_set_soft_start_time(sess,SS_TIME_3MS);
//VP boost converter current limit setting (by customer)
fp9931_set_vp_curr_limit(sess,CURR_LIMIT_3P5A);
//VN boost converter current limit setting (by customer)
fp9931_set_vn_curr_limit(sess,CURR_LIMIT_3P5A);
//gpiod_direction_output(sess->ebc_pmic_on, 0);
mutex_unlock(&sess->power_lock);
}
void __maybe_unused fp9931_resume_init(struct papyrus_sess *sess)
{
mutex_lock(&sess->power_lock);
//gpiod_direction_output(sess->ebc_pwr_en, 1);
msleep(10);
//gpiod_direction_output(sess->ebc_pmic_on, 1);
//msleep(10);
//gpiod_direction_output(sess->ebc_pwr_en_ts, 1);
//msleep(10);
//gpiod_direction_output(sess->ebc_pwr_pg, 1);
//msleep(10);
//update temperature
//fp9931_update_temperature(sess);
//initial VCOM setting (by customer)
fp9931_set_vcom_adjust(sess,vcom_default);
//initial VP/VN setting (by customer)
fp9931_set_vpvn_adjust(sess,VP_VN_VALUE_DEFAULT);
//initial power on delay (by customer)
fp9931_set_power_on_delay(sess,PON_DELAY_TD1_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD2_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD3_INDEX, DELAY_0MS);
fp9931_set_power_on_delay(sess,PON_DELAY_TD4_INDEX, DELAY_0MS);
//PMIC soft start time setting (by customer)
fp9931_set_soft_start_time(sess,SS_TIME_3MS);
//VP boost converter current limit setting (by customer)
fp9931_set_vp_curr_limit(sess,CURR_LIMIT_3P5A);
//VN boost converter current limit setting (by customer)
fp9931_set_vn_curr_limit(sess,CURR_LIMIT_3P5A);
//gpiod_direction_output(sess->ebc_pmic_on, 0);
mutex_unlock(&sess->power_lock);
}
static int papyrus_fp9931_probe(struct ebc_pmic *pmic, struct i2c_client *client)
{
struct papyrus_sess *sess;
//int stat;
sess = devm_kzalloc(&client->dev, sizeof(*sess), GFP_KERNEL);
if (!sess) {
dev_err(&client->dev, "%s:%d: kzalloc failed\n", __func__, __LINE__);
return -ENOMEM;
}
sess->client = client;
mutex_init(&sess->power_lock);
papyrus_hw_arg_init(sess);
sess->ebc_pmic_on = devm_gpiod_get_optional(&client->dev, "ebc_pmic_on", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(sess->ebc_pmic_on)) {
dev_err(&client->dev, "fp9931: failed to find poweren pin, no defined\n");
}
sess->ebc_pwr_en = devm_gpiod_get_optional(&client->dev, "ebc_pwr_en", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(sess->ebc_pwr_en)) {
dev_err(&client->dev, "fp9931: failed to find wakeup pin\n");
return -ENOMEM;
}
sess->ebc_pwr_en_ts = devm_gpiod_get_optional(&client->dev, "ebc_pwr_en_ts", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(sess->ebc_pwr_en_ts)) {
dev_err(&client->dev, "fp9931: failed to find vcom_ctl pin\n");
return -ENOMEM;
}
sess->ebc_pwr_pg = devm_gpiod_get_optional(&client->dev, "ebc_pwr_pg", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(sess->ebc_pwr_pg))
dev_err(&client->dev, "fp9931: no pwr_up pin find\n");
pmic->drvpar = sess;
pmic->pmic_get_vcom = papyrus_hw_vcom_get;
pmic->pmic_set_vcom = papyrus_hw_vcom_set;
pmic->pmic_pm_resume = papyrus_pm_resume;
pmic->pmic_pm_suspend = papyrus_pm_sleep;
pmic->pmic_power_req = papyrus_hw_power_req;
pmic->pmic_read_temperature = papyrus_hw_read_temperature;
sess->tmp_monitor_wq = alloc_ordered_workqueue("%s",
WQ_MEM_RECLAIM | WQ_FREEZABLE, "tps-tmp-monitor-wq");
INIT_DELAYED_WORK(&sess->tmp_delay_work, papyrus_tmp_work);
queue_delayed_work(sess->tmp_monitor_wq, &sess->tmp_delay_work,
msecs_to_jiffies(10000));
fp9931_init(sess);
return 0;
}
static int fp9931_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct ebc_pmic *pmic;
printk("--chenfujun fp9931_probe start");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "i2c check functionality failed.");
return -ENODEV;
}
pmic = devm_kzalloc(&client->dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic) {
dev_err(&client->dev, "%s:%d: kzalloc failed\n", __func__, __LINE__);
return -ENOMEM;
}
if (0 != papyrus_fp9931_probe(pmic, client)) {
dev_err(&client->dev, "fp9931 hw init failed.");
return -ENODEV;
}
pmic->dev = &client->dev;
sprintf(pmic->pmic_name, "fp9931");
i2c_set_clientdata(client, pmic);
dev_info(&client->dev, "fp9931 probe ok.\n");
printk("--chenfujun fp9931_probe end ");
return 0;
}
static int fp9931_remove(struct i2c_client *client)
{
struct ebc_pmic *pmic = i2c_get_clientdata(client);
struct papyrus_sess *sess = pmic->drvpar;
if (sess->tmp_monitor_wq)
destroy_workqueue(sess->tmp_monitor_wq);
return 0;
}
static const struct i2c_device_id fp9931_id[] = {
{ "fp9931", 0 },
{ }
};
static const struct of_device_id fp9931_dt_ids[] = {
{ .compatible = "ti,fp9931", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fp9931_dt_ids);
static struct i2c_driver fp9931_driver = {
.probe = fp9931_probe,
.remove = fp9931_remove,
.id_table = fp9931_id,
.driver = {
.of_match_table = fp9931_dt_ids,
.name = "fp9931",
.owner = THIS_MODULE,
},
};
module_i2c_driver(fp9931_driver);
MODULE_DESCRIPTION("ti fp9931 pmic");
MODULE_LICENSE("GPL");