fp9931 内核驱动调试

(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 = &regaddr,
		},
		{
			.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");

fp9931 单片机和linux 参考代码

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baidu_37552881

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值