fet_control: Add fet_control driver, full_current_en

Add fet control to enable low-z batt2 path after both batteries
have had tome to equalize under the balance_en_n setting.
EVB does not have an adc-in to read batt2 level directly, support
will be added on EVT1 hw. So until then, check for vbatt_main at
mid Voltage to cutover the battplus_en path, while current is low
i.e. < 250mA.

Change-Id: I53f5c13c881dd0cf0cf93af8c153f866dfcfad6d
Signed-off-by: Bernie Moreno-BERNIEM <berniem@motorola.com>
Reviewed-on: https://gerrit.mot.com/2057382
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Ling Jin <lingjin@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Bernie Moreno-BERNIEM 2021-09-01 18:49:33 -05:00 • committed by Bernie Moreno
commit f9df0a9f8f
4 changed files with 92 additions and 209 deletions

View file

@ -5,5 +5,5 @@ include $(CLEAR_VARS)
LOCAL_MODULE := fet_control.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -1,5 +1,6 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/fet_control
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include
obj-m += fet_control.o

View file

@ -0,0 +1,14 @@
all: modules
modules:
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
%:
$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
clean:
rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
rm -rf .tmp_versions

View file

@ -37,18 +37,18 @@
#include <linux/delay.h>
#define RBALANCE_VDIFF_MV 100
#define MAIN_MV_MID_LO 3600
#define MAIN_MV_MID_HI 3800
#define MAIN_CURR_SWITCH 250
#define GPIO_SET_DELAY 50
#define CHRG_FULLCURR_EN 1
#define CHRG_FULLCURR_DIS 0
#define HBDLY_DISCHARGE_MS 30000
#define HBDLY_DISCHARGE_MS 60000
#define HBDLY_CHARGE_MS 6000
static struct fet_control_data {
struct device *dev;
struct power_supply *flip_batt_psy;
struct power_supply *main_batt_psy;
struct power_supply *usb_psy;
struct notifier_block ps_notif;
struct delayed_work update;
bool init_done;
bool ps_is_present;
@ -57,13 +57,11 @@ static struct fet_control_data {
int battplus_en_gpio;
int balance_en_n_gpio;
int chrg_fullcurr_en_gpio;
int flip_chrg_en_gpio;
} fet_control_data;
} fetControlData;
int battplus_state = -1;
int balance_state = -1;
int fullcurr_state = -1;
int flip_chrg_en_state = -1;
static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property prop)
{
@ -108,22 +106,22 @@ static int set_battplus_state(const char *val, const struct kernel_param *kp)
int rc;
long mode;
if (!fet_control_data.init_done)
if (!fetControlData.init_done)
return -ENODEV;
rc = kstrtol(val, 0, &mode);
if (rc)
return rc;
rc = update_state_gpio(fet_control_data.battplus_en_gpio, !!mode);
rc = update_state_gpio(fetControlData.battplus_en_gpio, !!mode);
if (rc) {
pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.battplus_en_gpio);
pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.battplus_en_gpio);
return rc;
} else {
battplus_state = !!mode;
pr_debug("Set using flip_battplus gpio[%d], VAL:[%d]\n",
fet_control_data.battplus_en_gpio,
gpio_get_value(fet_control_data.battplus_en_gpio));
fetControlData.battplus_en_gpio,
gpio_get_value(fetControlData.battplus_en_gpio));
}
return 0;
@ -134,22 +132,22 @@ static int set_balance_state(const char *val, const struct kernel_param *kp)
int rc;
long mode;
if (!fet_control_data.init_done)
if (!fetControlData.init_done)
return -ENODEV;
rc = kstrtol(val, 0, &mode);
if (rc)
return rc;
rc = update_state_gpio(fet_control_data.balance_en_n_gpio, !!mode);
rc = update_state_gpio(fetControlData.balance_en_n_gpio, !!mode);
if (rc) {
pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.balance_en_n_gpio);
pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.balance_en_n_gpio);
return rc;
} else {
balance_state = !!mode;
pr_debug("Set using flip_balance_enn gpio[%d], VAL:[%d]\n",
fet_control_data.balance_en_n_gpio,
gpio_get_value(fet_control_data.balance_en_n_gpio));
fetControlData.balance_en_n_gpio,
gpio_get_value(fetControlData.balance_en_n_gpio));
}
return 0;
@ -160,7 +158,7 @@ static int set_fullcurr_state(const char *val, const struct kernel_param *kp)
int rc;
long mode;
if (!fet_control_data.init_done)
if (!fetControlData.init_done)
return -ENODEV;
rc = kstrtol(val, 0, &mode);
@ -168,15 +166,15 @@ static int set_fullcurr_state(const char *val, const struct kernel_param *kp)
return rc;
rc = update_state_gpio(fet_control_data.chrg_fullcurr_en_gpio, !!mode);
rc = update_state_gpio(fetControlData.chrg_fullcurr_en_gpio, !!mode);
if (rc) {
pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.chrg_fullcurr_en_gpio);
pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.chrg_fullcurr_en_gpio);
return rc;
} else {
fullcurr_state = !!mode;
pr_debug("Set using Chrg fullcurr_en gpio[%d], VAL:[%d]\n",
fet_control_data.chrg_fullcurr_en_gpio,
gpio_get_value(fet_control_data.chrg_fullcurr_en_gpio));
fetControlData.chrg_fullcurr_en_gpio,
gpio_get_value(fetControlData.chrg_fullcurr_en_gpio));
}
return 0;
@ -221,37 +219,20 @@ module_param_cb(fullcurr_state,
static void update_work(struct work_struct *work)
{
struct fet_control_data *data = container_of(work, struct fet_control_data, update.work);
int flip_curr, main_curr, flip_mv, main_mv, usbtype;
int flip_dischg = 1;
int main_curr, main_mv;
int main_dischg = 1;
int hb_sched_time = HBDLY_DISCHARGE_MS;
bool weakchrg = 0;
const char * usb_types[] = {"UNKNOWN","SDP","DCP","CDP","ACA","Type-C","PD","PD_DRP"};
usbtype = get_ps_int_prop(data->usb_psy,
POWER_SUPPLY_PROP_USB_TYPE);
pr_info("USB-TYPE found:%d [%s]\n", usbtype,usb_types[usbtype] );
if (usbtype == POWER_SUPPLY_USB_TYPE_SDP) {
pr_info("USB_TYPE WEAK-CHRG\n");
weakchrg = 1;
}
fullcurr_state = CHRG_FULLCURR_EN;
flip_mv = get_ps_int_prop(data->flip_batt_psy,
/*flip_mv = get_ps_int_prop(data->flip_batt_psy,
POWER_SUPPLY_PROP_VOLTAGE_NOW);
flip_mv /= 1000;
flip_mv /= 1000;*/
main_mv = get_ps_int_prop(data->main_batt_psy,
POWER_SUPPLY_PROP_VOLTAGE_NOW);
main_mv /= 1000;
flip_curr = get_ps_int_prop(data->flip_batt_psy,
POWER_SUPPLY_PROP_CURRENT_NOW);
flip_curr /= 1000;
if (flip_curr < 0) {
pr_info("Charging Flip\n");
flip_dischg = -1;
}
/* Main current sysfs already in mA */
main_curr = get_ps_int_prop(data->main_batt_psy,
POWER_SUPPLY_PROP_CURRENT_NOW);
@ -260,15 +241,6 @@ static void update_work(struct work_struct *work)
main_dischg = -1;
}
/* Max Allowed Charge Curr= 1C */
data->flip_chg_curr_max = get_ps_int_prop(data->flip_batt_psy,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN);
data->flip_chg_curr_max /= 1000;
if (data->flip_chg_curr_max < 0) {
pr_err("Failed to get Flip Max chrg curr\n");
return;
}
data->main_chg_curr_max = get_ps_int_prop(data->main_batt_psy,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN);
data->main_chg_curr_max /= 1000;
@ -277,144 +249,71 @@ static void update_work(struct work_struct *work)
return;
}
if (flip_dischg * flip_curr > data->flip_chg_curr_max ||
main_dischg * main_curr > data->main_chg_curr_max) {
fullcurr_state = CHRG_FULLCURR_DIS;
} else {
fullcurr_state = CHRG_FULLCURR_EN;
}
pr_info("Flip_curr:%d, flip_Ichg_MAX:%d, fullcurr state:%d\n",
flip_curr, data->flip_chg_curr_max, fullcurr_state);
/*pr_info("Flip_curr:%d, flip_Ichg_MAX:%d, fullcurr state:%d\n",
flip_curr, data->flip_chg_curr_max, fullcurr_state);*/
pr_info("Main_curr:%d, main_Ichg_MAX:%d\n",
main_curr, data->main_chg_curr_max);
pr_info("Flip-Vbatt:%d, Main-Vbatt: %d\n", flip_mv, main_mv);
pr_info("Flip-Vbatt: --, Main-Vbatt: %d\n", main_mv);
/* FET paths set in Batt discharge state */
if (weakchrg || (flip_dischg ==1 && main_dischg ==1)) {
if (main_dischg == 1) {
/* Leave Balance dflt-en & toggle in/out parallel low-Z battplus fet */
balance_state = 0;
if ((main_mv - flip_mv) < RBALANCE_VDIFF_MV) {
/* if ((main_mv - flip_mv) < RBALANCE_VDIFF_MV) { */
/* MMI_STOPSHIP power: Temproary W/A for EVB, without ADC Input
* Switch battplus_en Low-Z path at MAIN-Mid-Vbatt*/
if ( (main_mv >= MAIN_MV_MID_LO && main_mv <= MAIN_MV_MID_HI) &&
(main_curr/1000 < MAIN_CURR_SWITCH) ) {
battplus_state = 1;
pr_info("battplus-EN: %d\n", battplus_state);
} else {
battplus_state = 0;
pr_info("battplus-DIS: %d\n", battplus_state);
schedule_delayed_work(&data->update, msecs_to_jiffies(hb_sched_time));
}
if (weakchrg) {
// Only pm8350b enabled for weak charger type:
hb_sched_time = HBDLY_CHARGE_MS;
flip_chrg_en_state = 0;
}
} else {
hb_sched_time = HBDLY_CHARGE_MS;
battplus_state = 0;
balance_state = 1;
flip_chrg_en_state = 1;
pr_info("FASTCHG: battfets-OPEN\n");
}
pr_info("Flip chrg_en state: %d\n", flip_chrg_en_state);
/* Update GPIO controls */
update_state_gpio(fet_control_data.battplus_en_gpio, battplus_state);
update_state_gpio(fet_control_data.balance_en_n_gpio, balance_state);
update_state_gpio(fet_control_data.chrg_fullcurr_en_gpio, fullcurr_state);
update_state_gpio(fet_control_data.flip_chrg_en_gpio, flip_chrg_en_state);
schedule_delayed_work(&data->update, msecs_to_jiffies(hb_sched_time));
}
static int ps_notify_callback(struct notifier_block *nb,
unsigned long event, void *p)
{
struct fet_control_data *data = container_of(nb, struct fet_control_data, ps_notif);
struct power_supply *psy = p;
union power_supply_propval pval = {0};
int retval;
bool present_ps;
if ((event == PSY_EVENT_PROP_CHANGED) &&
psy && psy->desc->get_property && psy->desc->name &&
!strncmp(psy->desc->name, "usb", sizeof("usb")) && data) {
pr_info("psy notif: event = %lu\n", event);
retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
&pval);
if (retval) {
pr_err("%s psy get property failed, ERR: %d\n", psy->desc->name, retval);
return retval;
}
present_ps = (pval.intval) ? true : false;
pr_info("%s is %s\n", psy->desc->name,
(present_ps) ? "present" : "not present");
if (event == PSY_EVENT_PROP_CHANGED) {
if (data->ps_is_present == present_ps) {
pr_info("ps present state unchanged\n");
return 0;
}
}
data->ps_is_present = present_ps;
/* FETS OPEN prior to reading Vbatt levels */
battplus_state = 0;
balance_state = 1;
update_state_gpio(fet_control_data.battplus_en_gpio, battplus_state);
update_state_gpio(fet_control_data.balance_en_n_gpio, balance_state);
pr_info("VBATT CHK... battfets-OPEN\n");
cancel_delayed_work(&data->update);
schedule_delayed_work(&data->update, msecs_to_jiffies(3000));
}
return 0;
update_state_gpio(fetControlData.battplus_en_gpio, battplus_state);
update_state_gpio(fetControlData.balance_en_n_gpio, balance_state);
update_state_gpio(fetControlData.chrg_fullcurr_en_gpio, fullcurr_state);
}
static int parse_dt(struct device_node *node)
{
int rc = 0;
const char *flip_batt_psy_name, *main_batt_psy_name;
const char *main_batt_psy_name;
rc = of_property_read_string(node, "mmi,flip-batt-psy", &flip_batt_psy_name);
rc |= of_property_read_string(node, "mmi,main-batt-psy", &main_batt_psy_name);
if (!rc && flip_batt_psy_name && main_batt_psy_name) {
fet_control_data.flip_batt_psy = power_supply_get_by_name(flip_batt_psy_name);
fet_control_data.main_batt_psy = power_supply_get_by_name(main_batt_psy_name);
if (!fet_control_data.flip_batt_psy || !fet_control_data.main_batt_psy) {
if (!rc && main_batt_psy_name) {
fetControlData.main_batt_psy = power_supply_get_by_name(main_batt_psy_name);
if (!fetControlData.main_batt_psy) {
pr_debug("Could not get flip batt psy and main batt psy, maybe we are early - defer.");
return -EPROBE_DEFER;
}
}
fet_control_data.battplus_en_gpio = of_get_named_gpio(node,
fetControlData.battplus_en_gpio = of_get_named_gpio(node,
"mmi,flip_battplus_en_gpio", 0);
if (!gpio_is_valid(fet_control_data.battplus_en_gpio)) {
if (!gpio_is_valid(fetControlData.battplus_en_gpio)) {
pr_err("battplus_en_gpio is not valid!\n");
return -ENODEV;
}
fet_control_data.balance_en_n_gpio = of_get_named_gpio(node,
fetControlData.balance_en_n_gpio = of_get_named_gpio(node,
"mmi,flip_balance_en_n_gpio", 0);
if (!gpio_is_valid(fet_control_data.balance_en_n_gpio)) {
if (!gpio_is_valid(fetControlData.balance_en_n_gpio)) {
pr_err("balance_en_n_gpio is not valid!\n");
return -ENODEV;
}
fet_control_data.chrg_fullcurr_en_gpio = of_get_named_gpio(node,
fetControlData.chrg_fullcurr_en_gpio = of_get_named_gpio(node,
"mmi,chrg-fullcurr-en-gpio", 0);
if (!gpio_is_valid(fet_control_data.chrg_fullcurr_en_gpio)) {
if (!gpio_is_valid(fetControlData.chrg_fullcurr_en_gpio)) {
pr_err("chrg-fullcurr-en-gpio is not valid!\n");
return -ENODEV;
}
fet_control_data.flip_chrg_en_gpio = of_get_named_gpio(node,
"mmi,flip-chrg-en-gpios", 0);
if (!gpio_is_valid(fet_control_data.flip_chrg_en_gpio)) {
pr_err("flip-chrg-en-gpios is not valid!\n");
return -ENODEV;
}
return rc;
}
@ -422,102 +321,73 @@ static int fet_control_probe(struct platform_device *pdev)
{
int rc = 1;
fet_control_data.init_done = 0;
fet_control_data.dev = &pdev->dev;
fetControlData.init_done = 0;
fetControlData.dev = &pdev->dev;
rc = parse_dt(fet_control_data.dev->of_node);
rc = parse_dt(fetControlData.dev->of_node);
if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("Failed to parse device tree\n");
goto fail;
}
fet_control_data.usb_psy = power_supply_get_by_name("usb");
if (!fet_control_data.usb_psy) {
pr_debug("Could not get usb psy, maybe we are early - defer.");
return -EPROBE_DEFER;
}
rc = gpio_request(fet_control_data.flip_chrg_en_gpio, "mmi,flip-chrg-en-gpios");
if (rc) {
pr_err("Failed request flip-chrg-en-gpios\n");
goto fail;
}
rc = gpio_request(fet_control_data.battplus_en_gpio, "mmi,flip_battplus_en_gpio");
rc = gpio_request(fetControlData.battplus_en_gpio, "mmi,flip_battplus_en_gpio");
if (rc) {
pr_err("Failed request battplus_en_gpio\n");
goto fail;
}
rc = gpio_request(fet_control_data.balance_en_n_gpio, "mmi,flip_balance_en_n_gpio");
rc = gpio_request(fetControlData.balance_en_n_gpio, "mmi,flip_balance_en_n_gpio");
if (rc) {
pr_err("Failed request balance_en_n_gpio\n");
goto fail;
}
rc = gpio_request(fet_control_data.chrg_fullcurr_en_gpio, "mmi,chrg-fullcurr-en-gpio");
rc = gpio_request(fetControlData.chrg_fullcurr_en_gpio, "mmi,chrg-fullcurr-en-gpio");
if (rc) {
pr_err("Failed request chrg-fullcurr-en-gpio\n");
goto fail;
}
/* Enable Flip BQ Chrg */
rc = gpio_direction_output(fet_control_data.flip_chrg_en_gpio, 1);
if (rc) {
pr_err("Unable to set bq25898_en [%d]\n", fet_control_data.flip_chrg_en_gpio);
goto fail;
}
/* Set default Flip Battery Path FETs: battplus path disable*/
rc = gpio_direction_output(fet_control_data.battplus_en_gpio, 0); //Path Disabled
rc = gpio_direction_output(fetControlData.battplus_en_gpio, 0); //Path Disabled
if (rc) {
pr_err("Unable to set DIR flip_battplus_en [%d]\n", fet_control_data.battplus_en_gpio);
pr_err("Unable to set DIR flip_battplus_en [%d]\n", fetControlData.battplus_en_gpio);
goto fail;
}
battplus_state = 0;
/* Set Flip Rbalance path enable */
rc = gpio_direction_output(fet_control_data.balance_en_n_gpio, 0); // Path Enabled
rc = gpio_direction_output(fetControlData.balance_en_n_gpio, 0); // Path Enabled
if (rc) {
pr_err("Unable to set DIR/VAL Flip_balance_en_n [%d]\n", fet_control_data.balance_en_n_gpio);
pr_err("Unable to set DIR/VAL Flip_balance_en_n [%d]\n", fetControlData.balance_en_n_gpio);
goto fail;
}
balance_state = 0;
/* Set charger fullcurr enable */
rc = gpio_direction_output(fet_control_data.chrg_fullcurr_en_gpio, 0); // ChrgFullCurr dflt Disable
rc = gpio_direction_output(fetControlData.chrg_fullcurr_en_gpio, 1); // ChrgFullCurr dflt Enable
if (rc) {
pr_err("Unable to set DIR/VAL chrg_fullcurr_en_gpio [%d]\n", fet_control_data.chrg_fullcurr_en_gpio);
pr_err("Unable to set DIR/VAL chrg_fullcurr_en_gpio [%d]\n", fetControlData.chrg_fullcurr_en_gpio);
goto fail;
}
fullcurr_state = 0;
fullcurr_state = CHRG_FULLCURR_EN;
pr_info("Flip_battplus_en Init GPIO:[%d], VAL:[%d]\n",
fet_control_data.battplus_en_gpio,
gpio_get_value(fet_control_data.battplus_en_gpio) );
fetControlData.battplus_en_gpio,
gpio_get_value(fetControlData.battplus_en_gpio) );
pr_info("Flip_balance_en_n Init GPIO:[%d], VAL:[%d]\n",
fet_control_data.balance_en_n_gpio,
gpio_get_value(fet_control_data.balance_en_n_gpio) );
fetControlData.balance_en_n_gpio,
gpio_get_value(fetControlData.balance_en_n_gpio) );
pr_info("chrg_fullcurr Init GPIO:[%d], VAL:[%d]\n",
fet_control_data.chrg_fullcurr_en_gpio,
gpio_get_value(fet_control_data.chrg_fullcurr_en_gpio) );
pr_info("Flip charge_en Init GPIO:[%d], VAL:[%d]\n",
fet_control_data.flip_chrg_en_gpio,
gpio_get_value(fet_control_data.flip_chrg_en_gpio) );
// Notify on plug/unplug
fet_control_data.ps_notif.notifier_call = ps_notify_callback;
if (power_supply_reg_notifier(&fet_control_data.ps_notif)) {
pr_err("Failed to register notifier\n");
goto fail;
}
fetControlData.chrg_fullcurr_en_gpio,
gpio_get_value(fetControlData.chrg_fullcurr_en_gpio) );
// Work to update the fet & charge current states.
INIT_DELAYED_WORK(&fet_control_data.update, update_work);
schedule_delayed_work(&fet_control_data.update, 1);
INIT_DELAYED_WORK(&fetControlData.update, update_work);
schedule_delayed_work(&fetControlData.update, 10);
fet_control_data.init_done = true;
fetControlData.init_done = true;
return 0;
fail:
return rc;
@ -526,13 +396,11 @@ fail:
static int fet_control_remove(struct platform_device *pdev)
{
if (fet_control_data.init_done) {
cancel_delayed_work(&fet_control_data.update);
power_supply_unreg_notifier(&fet_control_data.ps_notif);
gpio_free(fet_control_data.battplus_en_gpio);
gpio_free(fet_control_data.balance_en_n_gpio);
gpio_free(fet_control_data.chrg_fullcurr_en_gpio);
gpio_free(fet_control_data.flip_chrg_en_gpio);
if (fetControlData.init_done) {
cancel_delayed_work(&fetControlData.update);
gpio_free(fetControlData.battplus_en_gpio);
gpio_free(fetControlData.balance_en_n_gpio);
gpio_free(fetControlData.chrg_fullcurr_en_gpio);
}
return 0;