BigW Consortium Gitlab

Commit 9f01dc92 by Ashish Syal

Merge branch 'yellow_kernel_modules'

parents 72350539 bb2f2955
......@@ -91,6 +91,20 @@ red_wp77xx: legato_wp77xx
MANGOH_BOARD=RED \
mksys -t wp77xx $(MKSYS_ARGS_COMMON) $(MKSYS_ARGS_RED) mangOH.sdef
.PHONY: yellow_wp85
yellow_wp85: legato_wp85
TOOLCHAIN_DIR=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp85 dir) \
TOOLCHAIN_PREFIX=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp85 prefix) \
MANGOH_BOARD=YELLOW \
mksys -t wp85 $(MKSYS_ARGS_COMMON) $(MKSYS_ARGS_RED) mangOH.sdef
.PHONY: yellow_wp750x
yellow_wp750x: legato_wp750x
TOOLCHAIN_DIR=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp750x dir) \
TOOLCHAIN_PREFIX=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp750x prefix) \
MANGOH_BOARD=YELLOW \
mksys -t wp750x $(MKSYS_ARGS_COMMON) $(MKSYS_ARGS_RED) mangOH.sdef
.PHONY: yellow_wp76xx
yellow_wp76xx: legato_wp76xx
TOOLCHAIN_DIR=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp76xx dir) \
......@@ -98,6 +112,13 @@ yellow_wp76xx: legato_wp76xx
MANGOH_BOARD=YELLOW \
mksys -t wp76xx $(MKSYS_ARGS_COMMON) $(MKSYS_ARGS_YELLOW) mangOH.sdef
.PHONY: yellow_wp77xx
yellow_wp77xx: legato_wp77xx
TOOLCHAIN_DIR=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp77xx dir) \
TOOLCHAIN_PREFIX=$(shell $(LEGATO_ROOT)/bin/findtoolchain wp77xx prefix) \
MANGOH_BOARD=YELLOW \
mksys -t wp77xx $(MKSYS_ARGS_COMMON) $(MKSYS_ARGS_YELLOW) mangOH.sdef
.PHONY: clean
clean:
rm -rf build
menuconfig POWER_SUPPLY
bool "Power supply class support"
help
Say Y here to enable power supply class support. This allows
power supply (batteries, AC, USB) monitoring by userspace
via sysfs and uevent (if available) and/or APM kernel interface
(if selected below).
if POWER_SUPPLY
config CHARGER_BQ25601
tristate "TI BQ25601 battery charger driver"
depends on I2C && GPIOLIB
help
Say Y to enable support for the TI BQ25601 battery charger.
endif # POWER_SUPPLY
Driver files were copied from https://github.com/tibms/kernel-3.18/tree/pmic in revision 8094dd062881463a0866fe93cc890c8b946e1d21
sources:
{
bq2560x_charger.c
}
/*
* BQ2560x battery charging driver
*
* Copyright (C) 2013 Texas Instruments
*
* This package 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.
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _LINUX_BQ2560X_I2C_H
#define _LINUX_BQ2560X_I2C_H
#include <linux/power_supply.h>
struct bq2560x_charge_param {
int vlim;
int ilim;
int ichg;
int vreg;
};
enum stat_ctrl {
STAT_CTRL_STAT,
STAT_CTRL_ICHG,
STAT_CTRL_INDPM,
STAT_CTRL_DISABLE,
};
enum vboost {
BOOSTV_4850 = 4850,
BOOSTV_5000 = 5000,
BOOSTV_5150 = 5150,
BOOSTV_5300 = 5300,
};
enum iboost {
BOOSTI_500 = 500,
BOOSTI_1200 = 1200,
};
enum vac_ovp {
VAC_OVP_5500 = 5500,
VAC_OVP_6200 = 6200,
VAC_OVP_10500 = 10500,
VAC_OVP_14300 = 14300,
};
struct bq2560x_platform_data {
struct bq2560x_charge_param usb;
struct bq2560x_charge_param ta;
int iprechg;
int iterm;
enum stat_ctrl statctrl;
enum vboost boostv; // options are 4850,
enum iboost boosti; // options are 500mA, 1200mA
enum vac_ovp vac_ovp;
};
#endif
\ No newline at end of file
/*
* BQ2560x battery charging driver
*
* Copyright (C) 2013 Texas Instruments
*
* This package 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.
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#define pr_fmt(fmt) "bq2560x: %s: " fmt, __func__
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/err.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/debugfs.h>
#include <linux/bitops.h>
#include <linux/math64.h>
#include <linux/alarmtimer.h>
#include "bq2560x_reg.h"
#include "bq2560x.h"
#if 1
#undef pr_debug
#define pr_debug pr_err
#undef pr_info
#define pr_info pr_err
#undef dev_dbg
#define dev_dbg dev_err
#else
#undef pr_info
#define pr_info pr_debug
#endif
enum bq2560x_vbus_type {
BQ2560X_VBUS_NONE = REG08_VBUS_TYPE_NONE,
BQ2560X_VBUS_USB = REG08_VBUS_TYPE_USB,
BQ2560X_VBUS_ADAPTER = REG08_VBUS_TYPE_ADAPTER,
BQ2560X_VBUS_OTG = REG08_VBUS_TYPE_OTG,
};
enum bq2560x_part_no {
BQ25600 = 0x00,
BQ25601 = 0x02,
};
enum {
USER = BIT(0),
JEITA = BIT(1),
BATT_FC = BIT(2),
BATT_PRES = BIT(3),
};
enum wakeup_src {
WAKEUP_SRC_MONITOR = 0,
WAKEUP_SRC_JEITA,
WAKEUP_SRC_MAX,
};
#define WAKEUP_SRC_MASK (~(~0 << WAKEUP_SRC_MAX))
struct bq2560x_wakeup_source {
struct wakeup_source source;
unsigned long enabled_bitmap;
spinlock_t ws_lock;
};
enum bq2560x_charge_state {
CHARGE_STATE_IDLE = REG08_CHRG_STAT_IDLE,
CHARGE_STATE_PRECHG = REG08_CHRG_STAT_PRECHG,
CHARGE_STATE_FASTCHG = REG08_CHRG_STAT_FASTCHG,
CHARGE_STATE_CHGDONE = REG08_CHRG_STAT_CHGDONE,
};
struct bq2560x_otg_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
};
struct bq2560x {
struct device *dev;
struct i2c_client *client;
enum bq2560x_part_no part_no;
int revision;
int gpio_ce;
int vbus_type;
int status;
struct mutex data_lock;
struct mutex i2c_rw_lock;
struct mutex profile_change_lock;
struct mutex charging_disable_lock;
struct mutex irq_complete;
struct bq2560x_wakeup_source bq2560x_ws;
bool irq_waiting;
bool irq_disabled;
bool resume_completed;
bool batt_present;
bool usb_present;
bool batt_full;
bool charge_enabled;/* Register bit status */
bool otg_enabled;
bool batfet_enabled;
bool in_hiz;
bool dis_safety;
bool vindpm_triggered;
bool iindpm_triggered;
bool in_therm_regulation;
bool in_vsys_regulation;
bool power_good;
bool vbus_good;
bool topoff_active;
bool acov_triggered;
/* if use software jeita in case of NTC is connected to gauge */
bool software_jeita_supported;
bool jeita_active;
bool batt_hot;
bool batt_cold;
bool batt_warm;
bool batt_cool;
int batt_hot_degc;
int batt_warm_degc;
int batt_cool_degc;
int batt_cold_degc;
int hot_temp_hysteresis;
int cold_temp_hysteresis;
int batt_cool_ma;
int batt_warm_ma;
int batt_cool_mv;
int batt_warm_mv;
int batt_temp;
int jeita_ma;
int jeita_mv;
unsigned int thermal_levels;
unsigned int therm_lvl_sel;
unsigned int *thermal_mitigation;
int usb_psy_ma;
int charge_state;
int charging_disabled_status;
int fault_status;
int skip_writes;
int skip_reads;
struct bq2560x_platform_data* platform_data;
struct delayed_work discharge_jeita_work; /*normal no charge mode*/
struct delayed_work charge_jeita_work; /*charge mode jeita work*/
struct alarm jeita_alarm;
struct dentry *debug_root;
struct bq2560x_otg_regulator otg_vreg;
struct power_supply *usb_psy;
struct power_supply *bms_psy;
struct power_supply batt_psy;
};
static int BatteryTestStatus_enable = 0;
static int __bq2560x_read_reg(struct bq2560x* bq, u8 reg, u8 *data)
{
s32 ret;
ret = i2c_smbus_read_byte_data(bq->client, reg);
if (ret < 0) {
pr_err("i2c read fail: can't read from reg 0x%02X\n", reg);
return ret;
}
*data = (u8)ret;
return 0;
}
static int __bq2560x_write_reg(struct bq2560x* bq, int reg, u8 val)
{
s32 ret;
ret = i2c_smbus_write_byte_data(bq->client, reg, val);
if (ret < 0) {
pr_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n",
val, reg, ret);
return ret;
}
return 0;
}
static int bq2560x_read_byte(struct bq2560x *bq, u8 *data, u8 reg)
{
int ret;
if (bq->skip_reads) {
*data = 0;
return 0;
}
mutex_lock(&bq->i2c_rw_lock);
ret = __bq2560x_read_reg(bq, reg, data);
mutex_unlock(&bq->i2c_rw_lock);
return ret;
}
static int bq2560x_write_byte(struct bq2560x *bq, u8 reg, u8 data)
{
int ret;
if (bq->skip_writes) {
return 0;
}
mutex_lock(&bq->i2c_rw_lock);
ret = __bq2560x_write_reg(bq, reg, data);
mutex_unlock(&bq->i2c_rw_lock);
if (ret) {
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
}
return ret;
}
static int bq2560x_update_bits(struct bq2560x *bq, u8 reg,
u8 mask, u8 data)
{
int ret;
u8 tmp;
if (bq->skip_reads || bq->skip_writes)
return 0;
mutex_lock(&bq->i2c_rw_lock);
ret = __bq2560x_read_reg(bq, reg, &tmp);
if (ret) {
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
goto out;
}
tmp &= ~mask;
tmp |= data & mask;
ret = __bq2560x_write_reg(bq, reg, tmp);
if (ret) {
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
}
out:
mutex_unlock(&bq->i2c_rw_lock);
return ret;
}
static void bq2560x_stay_awake(struct bq2560x_wakeup_source *source,
enum wakeup_src wk_src)
{
unsigned long flags;
spin_lock_irqsave(&source->ws_lock, flags);
if (!__test_and_set_bit(wk_src, &source->enabled_bitmap)) {
__pm_stay_awake(&source->source);
pr_debug("enabled source %s, wakeup_src %d\n",
source->source.name, wk_src);
}
spin_unlock_irqrestore(&source->ws_lock, flags);
}
static void bq2560x_relax(struct bq2560x_wakeup_source *source,
enum wakeup_src wk_src)
{
unsigned long flags;
spin_lock_irqsave(&source->ws_lock, flags);
if (__test_and_clear_bit(wk_src, &source->enabled_bitmap) &&
!(source->enabled_bitmap & WAKEUP_SRC_MASK)) {
__pm_relax(&source->source);
pr_debug("disabled source %s\n", source->source.name);
}
spin_unlock_irqrestore(&source->ws_lock, flags);
pr_debug("relax source %s, wakeup_src %d\n",
source->source.name, wk_src);
}
static void bq2560x_wakeup_src_init(struct bq2560x *bq)
{
spin_lock_init(&bq->bq2560x_ws.ws_lock);
wakeup_source_init(&bq->bq2560x_ws.source, "bq2560x");
}
static int bq2560x_enable_otg(struct bq2560x *bq)
{
u8 val = REG01_OTG_ENABLE << REG01_OTG_CONFIG_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_01,
REG01_OTG_CONFIG_MASK, val);
}
static int bq2560x_disable_otg(struct bq2560x *bq)
{
u8 val = REG01_OTG_DISABLE << REG01_OTG_CONFIG_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_01,
REG01_OTG_CONFIG_MASK, val);
}
static int bq2560x_enable_charger(struct bq2560x *bq)
{
int ret;
u8 val = REG01_CHG_ENABLE << REG01_CHG_CONFIG_SHIFT;
ret = bq2560x_update_bits(bq, BQ2560X_REG_01, REG01_CHG_CONFIG_MASK, val);
return ret;
}
static int bq2560x_disable_charger(struct bq2560x *bq)
{
int ret;
u8 val = REG01_CHG_DISABLE << REG01_CHG_CONFIG_SHIFT;
ret = bq2560x_update_bits(bq, BQ2560X_REG_01, REG01_CHG_CONFIG_MASK, val);
return ret;
}
int bq2560x_set_chargecurrent(struct bq2560x *bq, int curr)
{
u8 ichg;
if (curr < REG02_ICHG_BASE)
curr = REG02_ICHG_BASE;
ichg = (curr - REG02_ICHG_BASE)/REG02_ICHG_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_02, REG02_ICHG_MASK,
ichg << REG02_ICHG_SHIFT);
}
int bq2560x_set_term_current(struct bq2560x *bq, int curr)
{
u8 iterm;
if (curr < REG03_ITERM_BASE)
curr = REG03_ITERM_BASE;
iterm = (curr - REG03_ITERM_BASE) / REG03_ITERM_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_03, REG03_ITERM_MASK,
iterm << REG03_ITERM_SHIFT);
}
int bq2560x_set_prechg_current(struct bq2560x *bq, int curr)
{
u8 iprechg;
if (curr < REG03_IPRECHG_BASE)
curr = REG03_IPRECHG_BASE;
iprechg = (curr - REG03_IPRECHG_BASE) / REG03_IPRECHG_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_03, REG03_IPRECHG_MASK,
iprechg << REG03_IPRECHG_SHIFT);
}
int bq2560x_set_chargevolt(struct bq2560x *bq, int volt)
{
u8 val;
if (volt < REG04_VREG_BASE)
volt = REG04_VREG_BASE;
val = (volt - REG04_VREG_BASE)/REG04_VREG_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_04, REG04_VREG_MASK,
val << REG04_VREG_SHIFT);
}
int bq2560x_set_input_volt_limit(struct bq2560x *bq, int volt)
{
u8 val;
if (volt < REG06_VINDPM_BASE)
volt = REG06_VINDPM_BASE;
val = (volt - REG06_VINDPM_BASE) / REG06_VINDPM_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_06, REG06_VINDPM_MASK,
val << REG06_VINDPM_SHIFT);
}
int bq2560x_set_input_current_limit(struct bq2560x *bq, int curr)
{
u8 val;
if (curr < REG00_IINLIM_BASE)
curr = REG00_IINLIM_BASE;
val = (curr - REG00_IINLIM_BASE) / REG00_IINLIM_LSB;
return bq2560x_update_bits(bq, BQ2560X_REG_00, REG00_IINLIM_MASK,
val << REG00_IINLIM_SHIFT);
}
int bq2560x_set_watchdog_timer(struct bq2560x *bq, u8 timeout)
{
u8 temp;
temp = (u8)(((timeout - REG05_WDT_BASE) / REG05_WDT_LSB) << REG05_WDT_SHIFT);
return bq2560x_update_bits(bq, BQ2560X_REG_05, REG05_WDT_MASK, temp);
}
EXPORT_SYMBOL_GPL(bq2560x_set_watchdog_timer);
int bq2560x_disable_watchdog_timer(struct bq2560x *bq)
{
u8 val = REG05_WDT_DISABLE << REG05_WDT_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_05, REG05_WDT_MASK, val);
}
EXPORT_SYMBOL_GPL(bq2560x_disable_watchdog_timer);
int bq2560x_reset_watchdog_timer(struct bq2560x *bq)
{
u8 val = REG01_WDT_RESET << REG01_WDT_RESET_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_01, REG01_WDT_RESET_MASK, val);
}
EXPORT_SYMBOL_GPL(bq2560x_reset_watchdog_timer);
int bq2560x_reset_chip(struct bq2560x *bq)
{
int ret;
u8 val = REG0B_REG_RESET << REG0B_REG_RESET_SHIFT;
ret = bq2560x_update_bits(bq, BQ2560X_REG_0B, REG0B_REG_RESET_MASK, val);
return ret;
}
EXPORT_SYMBOL_GPL(bq2560x_reset_chip);
int bq2560x_enter_hiz_mode(struct bq2560x *bq)
{
u8 val = REG00_HIZ_ENABLE << REG00_ENHIZ_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_00, REG00_ENHIZ_MASK, val);
}
EXPORT_SYMBOL_GPL(bq2560x_enter_hiz_mode);
int bq2560x_exit_hiz_mode(struct bq2560x *bq)
{
u8 val = REG00_HIZ_DISABLE << REG00_ENHIZ_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_00, REG00_ENHIZ_MASK, val);
}
EXPORT_SYMBOL_GPL(bq2560x_exit_hiz_mode);
int bq2560x_get_hiz_mode(struct bq2560x *bq, u8 *state)
{
u8 val;
int ret;
ret = bq2560x_read_byte(bq, &val, BQ2560X_REG_00);
if (ret)
return ret;
*state = (val & REG00_ENHIZ_MASK) >> REG00_ENHIZ_SHIFT;
return 0;
}
EXPORT_SYMBOL_GPL(bq2560x_get_hiz_mode);
static int bq2560x_enable_term(struct bq2560x* bq, bool enable)
{
u8 val;
int ret;
if (enable)
val = REG05_TERM_ENABLE << REG05_EN_TERM_SHIFT;
else
val = REG05_TERM_DISABLE << REG05_EN_TERM_SHIFT;
ret = bq2560x_update_bits(bq, BQ2560X_REG_05, REG05_EN_TERM_MASK, val);
return ret;
}
EXPORT_SYMBOL_GPL(bq2560x_enable_term);
int bq2560x_set_boost_current(struct bq2560x *bq, int curr)
{
u8 val;
val = REG02_BOOST_LIM_0P5A;
if (curr == BOOSTI_1200)
val = REG02_BOOST_LIM_1P2A;
return bq2560x_update_bits(bq, BQ2560X_REG_02, REG02_BOOST_LIM_MASK,
val << REG02_BOOST_LIM_SHIFT);
}
int bq2560x_set_boost_voltage(struct bq2560x *bq, int volt)
{
u8 val;
if (volt == BOOSTV_4850)
val = REG06_BOOSTV_4P85V;
else if (volt == BOOSTV_5150)
val = REG06_BOOSTV_5P15V;
else if (volt == BOOSTV_5300)
val = REG06_BOOSTV_5P3V;
else
val = REG06_BOOSTV_5V;
return bq2560x_update_bits(bq, BQ2560X_REG_06, REG06_BOOSTV_MASK,
val << REG06_BOOSTV_SHIFT);
}
static int bq2560x_set_acovp_threshold(struct bq2560x *bq, int volt)
{
u8 val;
if (volt == VAC_OVP_14300)
val = REG06_OVP_14P3V;
else if (volt == VAC_OVP_10500)
val = REG06_OVP_10P5V;
else if (volt == VAC_OVP_6200)
val = REG06_OVP_6P2V;
else
val = REG06_OVP_5P5V;
return bq2560x_update_bits(bq, BQ2560X_REG_06, REG06_OVP_MASK,
val << REG06_OVP_SHIFT);
}
static int bq2560x_set_stat_ctrl(struct bq2560x *bq, int ctrl)
{
u8 val;
val = ctrl;
return bq2560x_update_bits(bq, BQ2560X_REG_00, REG00_STAT_CTRL_MASK,
val << REG00_STAT_CTRL_SHIFT);
}
static int bq2560x_set_int_mask(struct bq2560x *bq, int mask)
{
u8 val;
val = mask;
return bq2560x_update_bits(bq, BQ2560X_REG_0A, REG0A_INT_MASK_MASK,
val << REG0A_INT_MASK_SHIFT);
}
static int bq2560x_enable_batfet(struct bq2560x *bq)
{
const u8 val = REG07_BATFET_ON << REG07_BATFET_DIS_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_07, REG07_BATFET_DIS_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_enable_batfet);
static int bq2560x_disable_batfet(struct bq2560x *bq)
{
const u8 val = REG07_BATFET_OFF << REG07_BATFET_DIS_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_07, REG07_BATFET_DIS_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_disable_batfet);
static int bq2560x_set_batfet_delay(struct bq2560x *bq, uint8_t delay)
{
u8 val;
if (delay == 0)
val = REG07_BATFET_DLY_0S;
else
val = REG07_BATFET_DLY_10S;
val <<= REG07_BATFET_DLY_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_07, REG07_BATFET_DLY_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_set_batfet_delay);
static int bq2560x_set_vdpm_bat_track(struct bq2560x *bq)
{
const u8 val = REG07_VDPM_BAT_TRACK_200MV << REG07_VDPM_BAT_TRACK_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_07, REG07_VDPM_BAT_TRACK_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_set_vdpm_bat_track);
static int bq2560x_enable_safety_timer(struct bq2560x *bq)
{
const u8 val = REG05_CHG_TIMER_ENABLE << REG05_EN_TIMER_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_05, REG05_EN_TIMER_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_enable_safety_timer);
static int bq2560x_disable_safety_timer(struct bq2560x *bq)
{
const u8 val = REG05_CHG_TIMER_DISABLE << REG05_EN_TIMER_SHIFT;
return bq2560x_update_bits(bq, BQ2560X_REG_05, REG05_EN_TIMER_MASK,
val);
}
EXPORT_SYMBOL_GPL(bq2560x_disable_safety_timer);
static int bq2560x_charging_disable(struct bq2560x *bq, int reason,
int disable)
{
int ret = 0;
int disabled;
mutex_lock(&bq->charging_disable_lock);
disabled = bq->charging_disabled_status;
pr_err("reason=%d requested_disable=%d disabled_status=%d\n",
reason, disable, disabled);
if (disable == true)
disabled |= reason;
else
disabled &= ~reason;
if (disabled && bq->charge_enabled)
ret = bq2560x_disable_charger(bq);
else if (!disabled && !bq->charge_enabled)
ret = bq2560x_enable_charger(bq);
if (ret) {
pr_err("Couldn't disable/enable charging for reason=%d ret=%d\n",
ret, reason);
} else {
bq->charging_disabled_status = disabled;
mutex_lock(&bq->data_lock);
bq->charge_enabled = !disabled;
mutex_unlock(&bq->data_lock);
}
mutex_unlock(&bq->charging_disable_lock);
return ret;
}
static struct power_supply *get_bms_psy(struct bq2560x *bq)
{
if (bq->bms_psy)
return bq->bms_psy;
bq->bms_psy = power_supply_get_by_name("bms");
if (!bq->bms_psy)
pr_debug("bms power supply not found\n");
return bq->bms_psy;
}
static int bq2560x_get_batt_property(struct bq2560x *bq,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct power_supply *bms_psy = get_bms_psy(bq);
int ret;
if (!bms_psy)
return -EINVAL;
ret = bms_psy->get_property(bms_psy, psp, val);
return ret;
}
static inline bool is_device_suspended(struct bq2560x *bq);
static int bq2560x_get_prop_charge_type(struct bq2560x *bq)
{
u8 val = 0;
if (is_device_suspended(bq))
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
bq2560x_read_byte(bq, &val, BQ2560X_REG_08);
val &= REG08_CHRG_STAT_MASK;
val >>= REG08_CHRG_STAT_SHIFT;
switch (val) {
case CHARGE_STATE_FASTCHG:
return POWER_SUPPLY_CHARGE_TYPE_FAST;
case CHARGE_STATE_PRECHG:
return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
case CHARGE_STATE_CHGDONE:
case CHARGE_STATE_IDLE:
return POWER_SUPPLY_CHARGE_TYPE_NONE;
default:
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
}
static int bq2560x_get_prop_batt_present(struct bq2560x *bq)
{
union power_supply_propval batt_prop = {0,};
int ret;
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_PRESENT, &batt_prop);
if (!ret){
mutex_lock(&bq->data_lock);
bq->batt_present = batt_prop.intval;
mutex_unlock(&bq->data_lock);
}
return ret;
}
static int bq2560x_get_prop_batt_full(struct bq2560x *bq)
{
union power_supply_propval batt_prop = {0,};
int ret;
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_STATUS, &batt_prop);
if (!ret) {
mutex_lock(&bq->data_lock);
bq->batt_full = (batt_prop.intval == POWER_SUPPLY_STATUS_FULL);
mutex_unlock(&bq->data_lock);
}
return ret;
}
static int bq2560x_get_prop_charge_status(struct bq2560x *bq)
{
union power_supply_propval batt_prop = {0,};
int ret;
u8 status;
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_STATUS, &batt_prop);
if (!ret && batt_prop.intval == POWER_SUPPLY_STATUS_FULL)
return POWER_SUPPLY_STATUS_FULL;
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_08);
if (ret) {
return POWER_SUPPLY_STATUS_UNKNOWN;
}
mutex_lock(&bq->data_lock);
bq->charge_state = (status & REG08_CHRG_STAT_MASK) >> REG08_CHRG_STAT_SHIFT;
mutex_unlock(&bq->data_lock);
if (bq->usb_present && bq->jeita_active
&& (bq->batt_warm || bq->batt_cool)
&& bq->charge_state == CHARGE_STATE_CHGDONE)
return POWER_SUPPLY_STATUS_FULL;
switch(bq->charge_state) {
case CHARGE_STATE_FASTCHG:
case CHARGE_STATE_PRECHG:
return POWER_SUPPLY_STATUS_CHARGING;
case CHARGE_STATE_CHGDONE:
return POWER_SUPPLY_STATUS_NOT_CHARGING;
case CHARGE_STATE_IDLE:
return POWER_SUPPLY_STATUS_DISCHARGING;
default:
return POWER_SUPPLY_STATUS_UNKNOWN;
}
}
static int bq2560x_get_prop_health(struct bq2560x *bq)
{
int ret;
union power_supply_propval batt_prop = {0,};
if (bq->software_jeita_supported) {
if (bq->jeita_active) {
if (bq->batt_hot)
ret = POWER_SUPPLY_HEALTH_OVERHEAT;
/* else if (bq->batt_warm)
ret = POWER_SUPPLY_HEALTH_WARM;
else if (bq->batt_cool)
ret = POWER_SUPPLY_HEALTH_COOL;
else if (bq->batt_cold)
ret = POWER_SUPPLY_HEALTH_COLD;*/
} else {
ret = POWER_SUPPLY_HEALTH_GOOD;
}
} else {/* get health status from gauge */
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_HEALTH, &batt_prop);
if (!ret)
ret = batt_prop.intval;
else
ret = POWER_SUPPLY_HEALTH_UNKNOWN;
}
return ret;
}
static enum power_supply_property bq2560x_charger_props[] = {
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_FULL,
//POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
POWER_SUPPLY_PROP_TECHNOLOGY,
//POWER_SUPPLY_PROP_RESISTANCE_ID,
};
void static runin_work(struct bq2560x *bq, int batt_capacity)
{
int rc;
printk("%s:BatteryTestStatus_enable = %d bq->usb_present = %d \n",
__func__,BatteryTestStatus_enable,bq->usb_present);
if (/*!bq->usb_present || */!BatteryTestStatus_enable) {
if (bq->in_hiz) {
rc = bq2560x_exit_hiz_mode(bq);
if (rc) {
dev_err(bq->dev, "Couldn't enable charge rc=%d\n", rc);
} else {
pr_err("Exit Hiz Successfully\n");
bq->in_hiz = false;
}
}
return;
}
if (batt_capacity >= 80) {
pr_debug("bq2560x_get_prop_batt_capacity > 80\n");
//rc = bq2560x_charging_disable(bq, USER, true);
if (!bq->in_hiz) {
rc = bq2560x_enter_hiz_mode(bq);
if (rc) {
dev_err(bq->dev, "Couldn't disenable charge rc=%d\n", rc);
} else {
pr_err("Enter Hiz Successfully\n");
bq->in_hiz = true;
}
}
} else if (batt_capacity < 60) {
pr_debug("bq2560x_get_prop_batt_capacity < 60\n");
//rc = bq2560x_charging_disable(bq, USER, false);
if (bq->in_hiz) {
rc = bq2560x_exit_hiz_mode(bq);
if (rc) {
dev_err(bq->dev, "Couldn't enable charge rc=%d\n", rc);
} else {
pr_err("Exit Hiz Successfully\n");
bq->in_hiz = false;
}
}
}
}
static int bq2560x_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct bq2560x *bq = container_of(psy, struct bq2560x, batt_psy);
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = bq2560x_get_prop_charge_type(bq);
pr_debug("POWER_SUPPLY_PROP_CHARGE_TYPE:%d\n", val->intval);
break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
val->intval = bq->charge_enabled;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = 3080;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = bq2560x_get_prop_charge_status(bq);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = bq2560x_get_prop_health(bq);
break;
case POWER_SUPPLY_PROP_CAPACITY:
bq2560x_get_batt_property(bq, psp, val);
runin_work(bq, val->intval);
break;
//case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
// val->intval = bq->therm_lvl_sel;
// break;
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_TECHNOLOGY:
//case POWER_SUPPLY_PROP_RESISTANCE_ID:
// return bq2560x_get_batt_property(bq, psp, val);
default:
return -EINVAL;
}
return 0;
}
//static int bq2560x_system_temp_level_set(struct bq2560x *bq, int);
static int bq2560x_charger_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct bq2560x *bq = container_of(psy,
struct bq2560x, batt_psy);
switch (prop) {
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
bq2560x_charging_disable(bq, USER, !val->intval);
power_supply_changed(&bq->batt_psy);
power_supply_changed(bq->usb_psy);
pr_info("POWER_SUPPLY_PROP_CHARGING_ENABLED: %s\n",
val->intval ? "enable" : "disable");
break;
//case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
// bq2560x_system_temp_level_set(bq, val->intval);
// break;
default:
return -EINVAL;
}
return 0;
}
static int bq2560x_charger_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
int ret;
switch (prop) {
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
//case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
// ret = 1;
// break;
default:
ret = 0;
break;
}
return ret;
}
static int bq2560x_update_charging_profile(struct bq2560x *bq)
{
int ret;
int chg_ma;
int chg_mv;
int icl;
int therm_ma;
union power_supply_propval prop = {0,};
if (!bq->usb_present)
return 0;
ret = bq->usb_psy->get_property(bq->usb_psy,
POWER_SUPPLY_PROP_TYPE, &prop);
if (ret < 0) {
pr_err("couldn't read USB TYPE property, ret=%d\n", ret);
return ret;
}
pr_err("charge type = %d\n", prop.intval);
mutex_lock(&bq->profile_change_lock);
if (bq->jeita_active) {
chg_ma = bq->jeita_ma;
chg_mv = bq->jeita_mv;
} else {
if (prop.intval == POWER_SUPPLY_TYPE_USB_DCP
|| prop.intval == POWER_SUPPLY_TYPE_USB_CDP) {
chg_ma = bq->platform_data->ta.ichg;
chg_mv = bq->platform_data->ta.vreg;
} else {
chg_ma = bq->platform_data->usb.ichg;
chg_mv = bq->platform_data->usb.vreg;
}
}
icl = bq->usb_psy_ma;
if (bq->usb_psy_ma < chg_ma) {
chg_ma = bq->usb_psy_ma;
}
if (bq->therm_lvl_sel > 0
&& bq->therm_lvl_sel < (bq->thermal_levels - 1))
/*
* consider thermal limit only when it is active and not at
* the highest level
*/
therm_ma = bq->thermal_mitigation[bq->therm_lvl_sel];
else
therm_ma = chg_ma;
chg_ma = min(therm_ma, chg_ma);
pr_err("charge volt = %d, charge curr = %d, input curr limit = %d\n",
chg_mv, chg_ma, icl);
ret = bq2560x_set_input_current_limit(bq, icl);
if (ret < 0)
pr_err("couldn't set input current limit, ret=%d\n", ret);
ret = bq2560x_set_input_volt_limit(bq, bq->platform_data->ta.vlim);
if (ret < 0)
pr_err("couldn't set input voltage limit, ret=%d\n", ret);
ret = bq2560x_set_chargevolt(bq, chg_mv);
if (ret < 0)
pr_err("couldn't set charge voltage ret=%d\n", ret);
ret = bq2560x_set_chargecurrent(bq, chg_ma);
if (ret < 0)
pr_err("couldn't set charge current, ret=%d\n", ret);
mutex_unlock(&bq->profile_change_lock);
return 0;
}
/*static int bq2560x_system_temp_level_set(struct bq2560x *bq,
int lvl_sel)
{
int ret = 0;
int prev_therm_lvl;
pr_err("lvl_sel=%d, bq->therm_lvl_sel = %d\n", lvl_sel, bq->therm_lvl_sel);
if (BatteryTestStatus_enable)
return 0;
if (!bq->thermal_mitigation) {
pr_err("Thermal mitigation not supported\n");
return -EINVAL;
}
if (lvl_sel < 0) {
pr_err("Unsupported level selected %d\n", lvl_sel);
return -EINVAL;
}
if (lvl_sel >= bq->thermal_levels) {
pr_err("Unsupported level selected %d forcing %d\n", lvl_sel,
bq->thermal_levels - 1);
lvl_sel = bq->thermal_levels - 1;
}
if (lvl_sel == bq->therm_lvl_sel)
return 0;
prev_therm_lvl = bq->therm_lvl_sel;
bq->therm_lvl_sel = lvl_sel;
ret = bq2560x_update_charging_profile(bq);
if (ret)
pr_err("Couldn't set USB current ret = %d\n", ret);
return ret;
}
*/
static void bq2560x_external_power_changed(struct power_supply *psy)
{
struct bq2560x *bq = container_of(psy, struct bq2560x, batt_psy);
union power_supply_propval prop = {0,};
int ret, current_limit = 0;
ret = bq->usb_psy->get_property(bq->usb_psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
if (ret < 0)
pr_err("could not read USB current_max property, ret=%d\n", ret);
else
current_limit = prop.intval / 1000;
pr_err("current_limit = %d\n", current_limit);
if (bq->usb_psy_ma != current_limit) {
bq->usb_psy_ma = current_limit;
bq2560x_update_charging_profile(bq);
}
ret = bq->usb_psy->get_property(bq->usb_psy,
POWER_SUPPLY_PROP_ONLINE, &prop);
if (ret < 0)
pr_err("could not read USB ONLINE property, ret=%d\n", ret);
else
pr_info("usb online status =%d\n", prop.intval);
ret = 0;
bq2560x_get_prop_charge_status(bq);
if (bq->usb_present /*&& bq->charge_state != CHARGE_STATE_IDLE*//* && bq->charge_enabled *//*!bq->charging_disabled_status*/
/*&& bq->usb_psy_ma != 0*/) {
if (prop.intval == 0){
pr_err("set usb online\n");
ret = power_supply_set_online(bq->usb_psy, true);
}
} else {
if (prop.intval == 1) {
pr_err("set usb offline\n");
ret = power_supply_set_online(bq->usb_psy, false);
}
}
if (ret < 0)
pr_info("could not set usb online state, ret=%d\n", ret);
}
static int bq2560x_psy_register(struct bq2560x *bq)
{
int ret;
bq->batt_psy.name = "battery";
bq->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
bq->batt_psy.properties = bq2560x_charger_props;
bq->batt_psy.num_properties = ARRAY_SIZE(bq2560x_charger_props);
bq->batt_psy.get_property = bq2560x_charger_get_property;
bq->batt_psy.set_property = bq2560x_charger_set_property;
bq->batt_psy.external_power_changed = bq2560x_external_power_changed;
bq->batt_psy.property_is_writeable = bq2560x_charger_is_writeable;
ret = power_supply_register(bq->dev, &bq->batt_psy);
if (ret < 0) {
pr_err("failed to register batt_psy:%d\n", ret);
return ret;
}
return 0;
}
static void bq2560x_psy_unregister(struct bq2560x *bq)
{
power_supply_unregister(&bq->batt_psy);
}
static int bq2560x_otg_regulator_enable(struct regulator_dev *rdev)
{
int ret;
struct bq2560x *bq = rdev_get_drvdata(rdev);
ret = bq2560x_enable_otg(bq);
if (ret) {
pr_err("Couldn't enable OTG mode ret=%d\n", ret);
} else {
bq->otg_enabled = true;
pr_info("bq2560x OTG mode Enabled!\n");
}
return ret;
}
static int bq2560x_otg_regulator_disable(struct regulator_dev *rdev)
{
int ret;
struct bq2560x *bq = rdev_get_drvdata(rdev);
ret = bq2560x_disable_otg(bq);
if (ret) {
pr_err("Couldn't disable OTG mode, ret=%d\n", ret);
} else {
bq->otg_enabled = false;
pr_info("bq2560x OTG mode Disabled\n");
}
return ret;
}
static int bq2560x_otg_regulator_is_enable(struct regulator_dev *rdev)
{
int ret;
u8 status;
u8 enabled;
struct bq2560x *bq = rdev_get_drvdata(rdev);
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_01);
if (ret)
return ret;
enabled = ((status & REG01_OTG_CONFIG_MASK) >> REG01_OTG_CONFIG_SHIFT);
return (enabled == REG01_OTG_ENABLE) ? 1 : 0;
}
struct regulator_ops bq2560x_otg_reg_ops = {
.enable = bq2560x_otg_regulator_enable,
.disable = bq2560x_otg_regulator_disable,
.is_enabled = bq2560x_otg_regulator_is_enable,
};
static int bq2560x_regulator_init(struct bq2560x *bq)
{
int ret = 0;
struct regulator_init_data *init_data;
struct regulator_config cfg = {};
init_data = of_get_regulator_init_data(bq->dev, bq->dev->of_node);
if (!init_data) {
dev_err(bq->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
if (init_data->constraints.name) {
bq->otg_vreg.rdesc.owner = THIS_MODULE;
bq->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
bq->otg_vreg.rdesc.ops = &bq2560x_otg_reg_ops;
bq->otg_vreg.rdesc.name = init_data->constraints.name;
pr_info("regualtor name = %s\n", bq->otg_vreg.rdesc.name);
cfg.dev = bq->dev;
cfg.init_data = init_data;
cfg.driver_data = bq;
cfg.of_node = bq->dev->of_node;
init_data->constraints.valid_ops_mask
|= REGULATOR_CHANGE_STATUS;
bq->otg_vreg.rdev = regulator_register(
&bq->otg_vreg.rdesc, &cfg);
if (IS_ERR(bq->otg_vreg.rdev)) {
ret = PTR_ERR(bq->otg_vreg.rdev);
bq->otg_vreg.rdev = NULL;
if (ret != -EPROBE_DEFER)
dev_err(bq->dev,
"OTG reg failed, rc=%d\n", ret);
}
}
return ret;
}
static int bq2560x_parse_jeita_dt(struct device *dev, struct bq2560x* bq)
{
struct device_node *np = dev->of_node;
int ret;
ret = of_property_read_u32(np,"ti,bq2560x,jeita-hot-degc",
&bq->batt_hot_degc);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-hot-degc\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-warm-degc",
&bq->batt_warm_degc);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-warm-degc\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-cool-degc",
&bq->batt_cool_degc);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-cool-degc\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-cold-degc",
&bq->batt_cold_degc);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-cold-degc\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-hot-hysteresis",
&bq->hot_temp_hysteresis);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-hot-hysteresis\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-cold-hysteresis",
&bq->cold_temp_hysteresis);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-cold-hysteresis\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-cool-ma",
&bq->batt_cool_ma);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-cool-ma\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-cool-mv",
&bq->batt_cool_mv);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-cool-mv\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-warm-ma",
&bq->batt_warm_ma);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-warm-ma\n");
return ret;
}
ret = of_property_read_u32(np,"ti,bq2560x,jeita-warm-mv",
&bq->batt_warm_mv);
if(ret) {
pr_err("Failed to read ti,bq2560x,jeita-warm-mv\n");
return ret;
}
bq->software_jeita_supported =
of_property_read_bool(np,"ti,bq2560x,software-jeita-supported");
return 0;
}
static struct bq2560x_platform_data* bq2560x_parse_dt(struct device *dev,
struct bq2560x * bq)
{
int ret;
struct device_node *np = dev->of_node;
struct bq2560x_platform_data* pdata;
pdata = devm_kzalloc(dev, sizeof(struct bq2560x_platform_data),
GFP_KERNEL);
if (!pdata) {
pr_err("Out of memory\n");
return NULL;
}
ret = of_property_read_u32(np, "ti,bq2560x,chip-enable-gpio", &bq->gpio_ce);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,chip-enable-gpio\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,usb-vlim",&pdata->usb.vlim);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,usb-vlim\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,usb-ilim",&pdata->usb.ilim);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,usb-ilim\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,usb-vreg",&pdata->usb.vreg);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,usb-vreg\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,usb-ichg",&pdata->usb.ichg);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,usb-ichg\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,ta-vlim",&pdata->ta.vlim);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,ta-vlim\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,ta-ilim",&pdata->ta.ilim);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,ta-ilim\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,ta-vreg",&pdata->ta.vreg);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,ta-vreg\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,ta-ichg",&pdata->ta.ichg);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,ta-ichg\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,stat-pin-ctrl",&pdata->statctrl);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,stat-pin-ctrl\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,precharge-current",&pdata->iprechg);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,precharge-current\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,termination-current",&pdata->iterm);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,termination-current\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,boost-voltage",&pdata->boostv);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,boost-voltage\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,boost-current",&pdata->boosti);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,boost-current\n");
}
ret = of_property_read_u32(np,"ti,bq2560x,vac-ovp-threshold",&pdata->vac_ovp);
if(ret) {
pr_err("Failed to read node of ti,bq2560x,vac-ovp-threshold\n");
}
if (of_find_property(np, "ti,thermal-mitigation",
&bq->thermal_levels)) {
bq->thermal_mitigation = devm_kzalloc(bq->dev,
bq->thermal_levels,
GFP_KERNEL);
if (bq->thermal_mitigation == NULL) {
pr_err("thermal mitigation kzalloc() failed.\n");
}
bq->thermal_levels /= sizeof(int);
ret = of_property_read_u32_array(np,
"ti,thermal-mitigation",
bq->thermal_mitigation, bq->thermal_levels);
if (ret) {
pr_err("Couldn't read thermal limits ret = %d\n", ret);
}
}
return pdata;
}
static void bq2560x_init_jeita(struct bq2560x *bq)
{
bq->batt_temp = -EINVAL;
/* set default value in case of dts read fail */
bq->batt_hot_degc = 600;
bq->batt_warm_degc = 450;
bq->batt_cool_degc = 100;
bq->batt_cold_degc = 0;
bq->hot_temp_hysteresis = 50;
bq->cold_temp_hysteresis = 50;
bq->batt_cool_ma = 400;
bq->batt_cool_mv = 4100;
bq->batt_warm_ma = 400;
bq->batt_warm_mv = 4100;
bq->software_jeita_supported = true;
/* DTS setting will overwrite above default value */
bq2560x_parse_jeita_dt(&bq->client->dev, bq);
}
static int bq2560x_init_device(struct bq2560x *bq)
{
int ret;
bq2560x_disable_watchdog_timer(bq);
bq2560x_enable_batfet(bq);
bq2560x_set_vdpm_bat_track(bq);
ret = bq2560x_set_stat_ctrl(bq, bq->platform_data->statctrl);
if (ret)
pr_err("Failed to set stat pin control mode, ret = %d\n",ret);
ret = bq2560x_set_prechg_current(bq, bq->platform_data->iprechg);
if (ret)
pr_err("Failed to set prechg current, ret = %d\n",ret);
ret = bq2560x_set_term_current(bq, bq->platform_data->iterm);
if (ret)
pr_err("Failed to set termination current, ret = %d\n",ret);
ret = bq2560x_set_boost_voltage(bq, bq->platform_data->boostv);
if (ret)
pr_err("Failed to set boost voltage, ret = %d\n",ret);
ret = bq2560x_set_boost_current(bq, bq->platform_data->boosti);
if (ret)
pr_err("Failed to set boost current, ret = %d\n",ret);
ret = bq2560x_set_acovp_threshold(bq, bq->platform_data->vac_ovp);
if (ret)
pr_err("Failed to set acovp threshold, ret = %d\n",ret);
ret = bq2560x_set_int_mask(bq, REG0A_IINDPM_INT_MASK | REG0A_VINDPM_INT_MASK);
if (ret)
pr_err("Failed to set vindpm and iindpm int mask\n");
ret = bq2560x_enable_charger(bq);
if (ret) {
pr_err("Failed to enable charger, ret = %d\n",ret);
} else {
bq->charge_enabled = true;
pr_err("Charger Enabled Successfully!\n");
}
return 0;
}
static int bq2560x_detect_device(struct bq2560x* bq)
{
int ret;
u8 data;
ret = bq2560x_read_byte(bq, &data, BQ2560X_REG_0B);
if(ret == 0){
bq->part_no = (data & REG0B_PN_MASK) >> REG0B_PN_SHIFT;
bq->revision = (data & REG0B_DEV_REV_MASK) >> REG0B_DEV_REV_SHIFT;
}
return ret;
}
static void bq2560x_check_jeita(struct bq2560x *bq)
{
int ret;
bool last_hot, last_warm, last_cool, last_cold;
bool chg_disabled_jeita, jeita_hot_cold;
union power_supply_propval batt_prop = {0,};
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_TEMP, &batt_prop);
if (!ret)
bq->batt_temp = batt_prop.intval;
if (bq->batt_temp == -EINVAL)
return;
last_hot = bq->batt_hot;
last_warm = bq->batt_warm;
last_cool = bq->batt_cool;
last_cold = bq->batt_cold;
if (bq->batt_temp >= bq->batt_hot_degc) {/* HOT */
if (!bq->batt_hot) {
bq->batt_hot = true;
bq->batt_warm = false;
bq->batt_cool = false;
bq->batt_cold = false;
bq->jeita_ma = 0;
bq->jeita_mv = 0;
}
} else if (bq->batt_temp >= bq->batt_warm_degc) {/* WARM */
if (!bq->batt_hot
||(bq->batt_temp < bq->batt_hot_degc - bq->hot_temp_hysteresis)){
bq->batt_hot = false;
bq->batt_warm = true;
bq->batt_cool = false;
bq->batt_cold = false;
bq->jeita_mv = bq->batt_warm_mv;
bq->jeita_ma = bq->batt_warm_ma;
}
} else if (bq->batt_temp < bq->batt_cold_degc) {/* COLD */
if (!bq->batt_cold) {
bq->batt_hot = false;
bq->batt_warm = false;
bq->batt_cool = false;
bq->batt_cold = true;
bq->jeita_ma = 0;
bq->jeita_mv = 0;
}
} else if (bq->batt_temp < bq->batt_cool_degc) {/* COOL */
if (!bq->batt_cold ||
(bq->batt_temp > bq->batt_cold_degc + bq->cold_temp_hysteresis)) {
bq->batt_hot = false;
bq->batt_warm = false;
bq->batt_cool = true;
bq->batt_cold = false;
bq->jeita_mv = bq->batt_cool_mv;
bq->jeita_ma = bq->batt_cool_ma;
}
} else {/* NORMAL */
bq->batt_hot = false;
bq->batt_warm = false;
bq->batt_cool = false;
bq->batt_cold = false;
}
bq->jeita_active = bq->batt_cool || bq->batt_hot ||
bq->batt_cold || bq->batt_warm;
if ((last_cold != bq->batt_cold) || (last_warm != bq->batt_warm) ||
(last_cool != bq->batt_cool) || (last_hot != bq->batt_hot)) {
bq2560x_update_charging_profile(bq);
power_supply_changed(&bq->batt_psy);
power_supply_changed(bq->usb_psy);
} else if (bq->batt_hot || bq->batt_cold) { /*continuely update event */
power_supply_changed(&bq->batt_psy);
power_supply_changed(bq->usb_psy);
}
jeita_hot_cold = bq->jeita_active && (bq->batt_hot || bq->batt_cold);
chg_disabled_jeita = !!(bq->charging_disabled_status & JEITA);
if (jeita_hot_cold ^ chg_disabled_jeita)
bq2560x_charging_disable(bq, JEITA, jeita_hot_cold);
}
static void bq2560x_check_batt_pres(struct bq2560x *bq)
{
int ret = 0;
bool chg_disabled_pres;
ret = bq2560x_get_prop_batt_present(bq);
if (!ret) {
chg_disabled_pres = !!(bq->charging_disabled_status & BATT_PRES);
if (chg_disabled_pres ^ !bq->batt_present) {
ret = bq2560x_charging_disable(bq, BATT_PRES, !bq->batt_present);
if (ret) {
pr_err("failed to %s charging, ret = %d\n",
bq->batt_present ? "disable" : "enable",
ret);
}
power_supply_changed(&bq->batt_psy);
power_supply_changed(bq->usb_psy);
}
}
}
static void bq2560x_check_batt_full(struct bq2560x *bq)
{
int ret = 0;
bool chg_disabled_fc;
ret = bq2560x_get_prop_batt_full(bq);
if (!ret) {
chg_disabled_fc = !!(bq->charging_disabled_status & BATT_FC);
if (chg_disabled_fc ^ bq->batt_full) {
ret = bq2560x_charging_disable(bq, BATT_FC, bq->batt_full);
if (ret) {
pr_err("failed to %s charging, ret = %d\n",
bq->batt_full ? "disable" : "enable",
ret);
}
power_supply_changed(&bq->batt_psy);
power_supply_changed(bq->usb_psy);
}
}
}
static int calculate_jeita_poll_interval(struct bq2560x* bq)
{
int interval;
if (bq->batt_hot || bq->batt_cold)
interval = 5;
else if (bq->batt_warm || bq->batt_cool)
interval = 10;
else
interval = 15;
return interval;
}
#define FG_LOG_INTERVAL 120
static void bq2560x_dump_fg_reg(struct bq2560x *bq)
{
union power_supply_propval val = {0,};
static int dump_cnt;
if (++dump_cnt >= (FG_LOG_INTERVAL / calculate_jeita_poll_interval(bq))) {
dump_cnt = 0;
val.intval = 0;
// bq->bms_psy->set_property(bq->bms_psy,
// POWER_SUPPLY_PROP_UPDATE_NOW, &val);
}
}
static enum alarmtimer_restart bq2560x_jeita_alarm_cb(struct alarm *alarm,
ktime_t now)
{
struct bq2560x *bq = container_of(alarm,
struct bq2560x, jeita_alarm);
unsigned long ns;
bq2560x_stay_awake(&bq->bq2560x_ws, WAKEUP_SRC_JEITA);
schedule_delayed_work(&bq->charge_jeita_work, HZ/2);
ns = calculate_jeita_poll_interval(bq) * 1000000000LL;
alarm_forward_now(alarm, ns_to_ktime(ns));
return ALARMTIMER_RESTART;
}
static void bq2560x_dump_status(struct bq2560x* bq);
static void bq2560x_charge_jeita_workfunc(struct work_struct *work)
{
struct bq2560x *bq = container_of(work,
struct bq2560x, charge_jeita_work.work);
bq2560x_reset_watchdog_timer(bq);
bq2560x_check_batt_pres(bq);
bq2560x_check_batt_full(bq);
bq2560x_dump_fg_reg(bq);
bq2560x_check_jeita(bq);
bq2560x_dump_status(bq);
bq2560x_relax(&bq->bq2560x_ws, WAKEUP_SRC_JEITA);
}
static void bq2560x_discharge_jeita_workfunc(struct work_struct *work)
{
struct bq2560x *bq = container_of(work,
struct bq2560x, discharge_jeita_work.work);
bq2560x_check_batt_pres(bq);
bq2560x_check_batt_full(bq);
bq2560x_dump_fg_reg(bq);
bq2560x_check_jeita(bq);
schedule_delayed_work(&bq->discharge_jeita_work,
calculate_jeita_poll_interval(bq) * HZ);
}
static const unsigned char* charge_stat_str[] = {
"Not Charging",
"Precharging",
"Fast Charging",
"Charge Done",
};
static void bq2560x_dump_status(struct bq2560x* bq)
{
u8 status;
u8 addr;
int ret;
u8 val;
union power_supply_propval batt_prop = {0,};
ret = bq2560x_get_batt_property(bq,
POWER_SUPPLY_PROP_CURRENT_NOW, &batt_prop);
if (!ret)
pr_err("FG current:%d\n", batt_prop.intval);
for (addr = 0x0; addr <= 0x0B; addr++) {
if (addr == 0x09) {
pr_err("bq Reg[09] = 0x%02X\n", bq->fault_status);
continue;
}
ret = bq2560x_read_byte(bq, &val, addr);
if (!ret)
pr_err("bq Reg[%02X] = 0x%02X\n", addr, val);
else
pr_err("bq Reg red err\n");
}
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_0A);
if (ret) {
pr_err("failed to read reg0a\n");
return;
}
mutex_lock(&bq->data_lock);
bq->vbus_good = !!(status & REG0A_VBUS_GD_MASK);
bq->vindpm_triggered = !!(status & REG0A_VINDPM_STAT_MASK);
bq->iindpm_triggered = !!(status & REG0A_IINDPM_STAT_MASK);
bq->topoff_active = !!(status & REG0A_TOPOFF_ACTIVE_MASK);
bq->acov_triggered = !!(status & REG0A_ACOV_STAT_MASK);
mutex_unlock(&bq->data_lock);
if (!bq->power_good)
pr_info("Power Poor\n");
if (!bq->vbus_good)
pr_err("Vbus voltage not good!\n");
if (bq->vindpm_triggered)
pr_err("VINDPM triggered\n");
if (bq->iindpm_triggered)
pr_err("IINDPM triggered\n");
if (bq->acov_triggered)
pr_err("ACOV triggered\n");
if (bq->fault_status & REG09_FAULT_WDT_MASK)
pr_err("Watchdog timer expired!\n");
if (bq->fault_status & REG09_FAULT_BOOST_MASK)
pr_err("Boost fault occurred!\n");
status = (bq->fault_status & REG09_FAULT_CHRG_MASK) >> REG09_FAULT_CHRG_SHIFT;
if (status == REG09_FAULT_CHRG_INPUT)
pr_err("input fault!\n");
else if (status == REG09_FAULT_CHRG_THERMAL)
pr_err("charge thermal shutdown fault!\n");
else if (status == REG09_FAULT_CHRG_TIMER)
pr_err("charge timer expired fault!\n");
if (bq->fault_status & REG09_FAULT_BAT_MASK)
pr_err("battery ovp fault!\n");
if (!bq->software_jeita_supported) {
status = (bq->fault_status & REG09_FAULT_NTC_MASK) >> REG09_FAULT_NTC_SHIFT;
if (status == REG09_FAULT_NTC_WARM)
pr_debug("JEITA ACTIVE: WARM\n");
else if (status == REG09_FAULT_NTC_COOL)
pr_debug("JEITA ACTIVE: COOL\n");
else if (status == REG09_FAULT_NTC_COLD)
pr_debug("JEITA ACTIVE: COLD\n");
else if (status == REG09_FAULT_NTC_HOT)
pr_debug("JEITA ACTIVE: HOT!\n");
} else if (bq->jeita_active) {
if (bq->batt_hot)
pr_debug("JEITA ACTIVE: HOT\n");
else if (bq->batt_warm)
pr_debug("JEITA ACTIVE: WARM\n");
else if (bq->batt_cool)
pr_debug("JEITA ACTIVE: COOL\n");
else if (bq->batt_cold)
pr_debug("JEITA ACTIVE: COLD\n");
}
pr_err("%s\n",charge_stat_str[bq->charge_state]);
}
static void bq2560x_update_status(struct bq2560x *bq)
{
u8 status;
int ret;
/* Read twice to get present status */
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_09);
if (ret)
return;
pr_err("First read of REG[09] = 0x%02x\n", status);
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_09);
if (ret)
return;
pr_err("Second read of REG[09] = 0x%02x\n", status);
mutex_lock(&bq->data_lock);
bq->fault_status = status;
mutex_unlock(&bq->data_lock);
}
static irqreturn_t bq2560x_charger_interrupt(int irq, void *dev_id)
{
struct bq2560x *bq = dev_id;
u8 status;
int ret;
mutex_lock(&bq->irq_complete);
bq->irq_waiting = true;
if (!bq->resume_completed) {
dev_dbg(bq->dev, "IRQ triggered before device-resume\n");
if (!bq->irq_disabled) {
disable_irq_nosync(irq);
bq->irq_disabled = true;
}
mutex_unlock(&bq->irq_complete);
return IRQ_HANDLED;
}
bq->irq_waiting = false;
ret = bq2560x_read_byte(bq, &status, BQ2560X_REG_08);
if (ret) {
mutex_unlock(&bq->irq_complete);
return IRQ_HANDLED;
}
mutex_lock(&bq->data_lock);
bq->power_good = !!(status & REG08_PG_STAT_MASK);
mutex_unlock(&bq->data_lock);
if(!bq->power_good) {
if(bq->usb_present) {
bq->usb_present = false;
power_supply_set_present(bq->usb_psy, bq->usb_present);
}
if (bq->software_jeita_supported) {
alarm_try_to_cancel(&bq->jeita_alarm);
}
bq2560x_disable_watchdog_timer(bq);
schedule_delayed_work(&bq->discharge_jeita_work,
calculate_jeita_poll_interval(bq) * HZ);
pr_err("usb removed, set usb present = %d\n", bq->usb_present);
} else if (bq->power_good && !bq->usb_present) {
bq->usb_present = true;
msleep(10);/*for cdp detect*/
power_supply_set_present(bq->usb_psy, bq->usb_present);
cancel_delayed_work(&bq->discharge_jeita_work);
if (bq->software_jeita_supported) {
ret = alarm_start_relative(&bq->jeita_alarm,
ns_to_ktime(1 * 1000000000LL));
if (ret)
pr_err("start alarm for JEITA detection failed, ret=%d\n",
ret);
}
bq2560x_set_watchdog_timer(bq, 80);
pr_err("usb plugged in, set usb present = %d\n", bq->usb_present);
}
bq2560x_update_status(bq);
mutex_unlock(&bq->irq_complete);
power_supply_changed(&bq->batt_psy);
return IRQ_HANDLED;
}
static void determine_initial_status(struct bq2560x *bq)
{
int ret;
u8 status = 0;
ret = bq2560x_get_hiz_mode(bq, &status);
if (!ret)
bq->in_hiz = !!status;
bq2560x_charger_interrupt(bq->client->irq, bq);
}
static ssize_t bq2560x_show_registers(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bq2560x *bq = dev_get_drvdata(dev);
u8 addr;
u8 val;
u8 tmpbuf[200];
int len;
int idx = 0;
int ret ;
idx = snprintf(buf, PAGE_SIZE, "%s:\n", "bq2560x Reg");
for (addr = 0x0; addr <= 0x0B; addr++) {
ret = bq2560x_read_byte(bq, &val, addr);
if (ret == 0) {
len = snprintf(tmpbuf, PAGE_SIZE - idx,"Reg[0x%.2x] = 0x%.2x\n", addr, val);
memcpy(&buf[idx], tmpbuf, len);
idx += len;
}
}
return idx;
}
static ssize_t bq2560x_store_registers(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct bq2560x *bq = dev_get_drvdata(dev);
int ret;
unsigned int reg;
unsigned int val;
ret = sscanf(buf, "%x %x", &reg, &val);
if (ret == 2 && reg < 0x0B) {
bq2560x_write_byte(bq, (unsigned char)reg, (unsigned char)val);
}
return count;
}
static ssize_t bq2560x_battery_test_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", BatteryTestStatus_enable);
}
static ssize_t bq2560x_battery_test_status_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned int input;
if (sscanf(buf, "%u", &input) != 1)
retval = -EINVAL;
else
BatteryTestStatus_enable = input;
pr_err("BatteryTestStatus_enable = %d\n", BatteryTestStatus_enable);
return retval;
}
static ssize_t bq2560x_show_hiz(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bq2560x *bq = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", bq->in_hiz);
}
static ssize_t bq2560x_store_hiz(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct bq2560x *bq = dev_get_drvdata(dev);
int ret;
unsigned int val;
ret = sscanf(buf, "%d", &val);
if (ret == 1) {
if (val)
ret = bq2560x_enter_hiz_mode(bq);
else
ret = bq2560x_exit_hiz_mode(bq);
}
if (!ret)
bq->in_hiz = !!val;
return ret;
}
static ssize_t bq2560x_show_dis_safety(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bq2560x *bq = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", bq->dis_safety);
}
static ssize_t bq2560x_store_dis_safety(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct bq2560x *bq = dev_get_drvdata(dev);
int ret;
unsigned int val;
ret = sscanf(buf, "%d", &val);
if (ret == 1) {
if (val)
ret = bq2560x_disable_safety_timer(bq);
else
ret = bq2560x_enable_safety_timer(bq);
}
if (!ret)
bq->dis_safety = !!val;
return count;
}
static DEVICE_ATTR(registers, S_IRUGO | S_IWUSR, bq2560x_show_registers, bq2560x_store_registers);
static DEVICE_ATTR(BatteryTestStatus, S_IRUGO | S_IWUSR, bq2560x_battery_test_status_show, bq2560x_battery_test_status_store);
static DEVICE_ATTR(hiz, S_IRUGO | S_IWUSR, bq2560x_show_hiz, bq2560x_store_hiz);
static DEVICE_ATTR(dissafety, S_IRUGO | S_IWUSR, bq2560x_show_dis_safety, bq2560x_store_dis_safety);
static struct attribute *bq2560x_attributes[] = {
&dev_attr_registers.attr,
&dev_attr_BatteryTestStatus.attr,
&dev_attr_hiz.attr,
&dev_attr_dissafety.attr,
NULL,
};
static const struct attribute_group bq2560x_attr_group = {
.attrs = bq2560x_attributes,
};
static int show_registers(struct seq_file *m, void *data)
{
struct bq2560x *bq = m->private;
u8 addr;
int ret;
u8 val;
for (addr = 0x0; addr <= 0x0B; addr++) {
ret = bq2560x_read_byte(bq, &val, addr);
if (!ret)
seq_printf(m, "Reg[%02X] = 0x%02X\n", addr, val);
}
return 0;
}
static int reg_debugfs_open(struct inode *inode, struct file *file)
{
struct bq2560x *bq = inode->i_private;
return single_open(file, show_registers, bq);
}
static const struct file_operations reg_debugfs_ops = {
.owner = THIS_MODULE,
.open = reg_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void create_debugfs_entry(struct bq2560x *bq)
{
bq->debug_root = debugfs_create_dir("bq2560x", NULL);
if (!bq->debug_root)
pr_err("Failed to create debug dir\n");
if (bq->debug_root) {
debugfs_create_file("registers", S_IFREG | S_IRUGO,
bq->debug_root, bq, &reg_debugfs_ops);
debugfs_create_x32("charging_disable_status", S_IFREG | S_IRUGO,
bq->debug_root, &(bq->charging_disabled_status));
debugfs_create_x32("fault_status", S_IFREG | S_IRUGO,
bq->debug_root, &(bq->fault_status));
debugfs_create_x32("vbus_type", S_IFREG | S_IRUGO,
bq->debug_root, &(bq->vbus_type));
debugfs_create_x32("charge_state", S_IFREG | S_IRUGO,
bq->debug_root, &(bq->charge_state));
debugfs_create_x32("skip_reads",
S_IFREG | S_IWUSR | S_IRUGO,
bq->debug_root,
&(bq->skip_reads));
debugfs_create_x32("skip_writes",
S_IFREG | S_IWUSR | S_IRUGO,
bq->debug_root,
&(bq->skip_writes));
}
}
static int bq2560x_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bq2560x *bq;
struct power_supply *usb_psy;
struct power_supply *bms_psy;
int ret;
usb_psy = power_supply_get_by_name("usb");
if (!usb_psy) {
dev_dbg(&client->dev, "USB supply not found, defer probe\n");
return -EPROBE_DEFER;
}
bms_psy = power_supply_get_by_name("bms");
if (!bms_psy) {
dev_dbg(&client->dev, "bms supply not found, defer probe\n");
return -EPROBE_DEFER;
}
bq = devm_kzalloc(&client->dev, sizeof(struct bq2560x), GFP_KERNEL);
if (!bq) {
pr_err("Out of memory\n");
return -ENOMEM;
}
bq->dev = &client->dev;
bq->usb_psy = usb_psy;
bq->bms_psy = bms_psy;
bq->client = client;
i2c_set_clientdata(client, bq);
mutex_init(&bq->i2c_rw_lock);
mutex_init(&bq->data_lock);
mutex_init(&bq->profile_change_lock);
mutex_init(&bq->charging_disable_lock);
mutex_init(&bq->irq_complete);
bq->resume_completed = true;
bq->irq_waiting = false;
ret = bq2560x_detect_device(bq);
if(ret) {
pr_err("No bq2560x device found!\n");
return -ENODEV;
}
bq2560x_init_jeita(bq);
if (client->dev.of_node)
bq->platform_data = bq2560x_parse_dt(&client->dev, bq);
else
bq->platform_data = client->dev.platform_data;
if (!bq->platform_data) {
pr_err("No platform data provided.\n");
return -EINVAL;
}
if (gpio_is_valid(bq->gpio_ce)) {
ret = devm_gpio_request(&client->dev, bq->gpio_ce, "bq2560x_ce");
if (ret) {
pr_err("Failed to request chip enable gpio %d:, err: %d\n", bq->gpio_ce, ret);
return ret;
}
gpio_direction_output(bq->gpio_ce, 0);
}
ret = bq2560x_init_device(bq);
if (ret) {
pr_err("Failed to init device\n");
return ret;
}
ret = bq2560x_psy_register(bq);
if (ret)
return ret;
ret = bq2560x_regulator_init(bq);
if (ret) {
pr_err("Couldn't initialize bq2560x regulator ret=%d\n", ret);
return ret;
}
INIT_DELAYED_WORK(&bq->charge_jeita_work, bq2560x_charge_jeita_workfunc);
INIT_DELAYED_WORK(&bq->discharge_jeita_work, bq2560x_discharge_jeita_workfunc);
alarm_init(&bq->jeita_alarm, ALARM_BOOTTIME, bq2560x_jeita_alarm_cb);
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
bq2560x_charger_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"bq2560x charger irq", bq);
if (ret < 0) {
pr_err("request irq for irq=%d failed, ret =%d\n", client->irq, ret);
goto err_1;
}
enable_irq_wake(client->irq);
}
bq2560x_wakeup_src_init(bq);
device_init_wakeup(bq->dev, 1);
create_debugfs_entry(bq);
ret = sysfs_create_group(&bq->dev->kobj, &bq2560x_attr_group);
if (ret) {
dev_err(bq->dev, "failed to register sysfs. err: %d\n", ret);
}
determine_initial_status(bq);
pr_err("bq2560x probe successfully, Part Num:%d, Revision:%d\n!",
bq->part_no, bq->revision);
return 0;
err_1:
bq2560x_psy_unregister(bq);
return ret;
}
static inline bool is_device_suspended(struct bq2560x *bq)
{
return !bq->resume_completed;
}
static int bq2560x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq2560x *bq = i2c_get_clientdata(client);
mutex_lock(&bq->irq_complete);
bq->resume_completed = false;
mutex_unlock(&bq->irq_complete);
return 0;
}
static int bq2560x_suspend_noirq(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq2560x *bq = i2c_get_clientdata(client);
if (bq->irq_waiting) {
pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
return -EBUSY;
}
return 0;
}
static int bq2560x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq2560x *bq = i2c_get_clientdata(client);
mutex_lock(&bq->irq_complete);
bq->resume_completed = true;
if (bq->irq_waiting) {
bq->irq_disabled = false;
enable_irq(client->irq);
mutex_unlock(&bq->irq_complete);
bq2560x_charger_interrupt(client->irq, bq);
} else {
mutex_unlock(&bq->irq_complete);
}
power_supply_changed(&bq->batt_psy);
return 0;
}
static int bq2560x_charger_remove(struct i2c_client *client)
{
struct bq2560x *bq = i2c_get_clientdata(client);
alarm_try_to_cancel(&bq->jeita_alarm);
cancel_delayed_work_sync(&bq->charge_jeita_work);
cancel_delayed_work_sync(&bq->discharge_jeita_work);
regulator_unregister(bq->otg_vreg.rdev);
bq2560x_psy_unregister(bq);
mutex_destroy(&bq->charging_disable_lock);
mutex_destroy(&bq->profile_change_lock);
mutex_destroy(&bq->data_lock);
mutex_destroy(&bq->i2c_rw_lock);
mutex_destroy(&bq->irq_complete);
debugfs_remove_recursive(bq->debug_root);
sysfs_remove_group(&bq->dev->kobj, &bq2560x_attr_group);
return 0;
}
static void bq2560x_charger_shutdown(struct i2c_client *client)
{
}
static struct of_device_id bq2560x_charger_match_table[] = {
{.compatible = "ti,bq25600-charger",},
{.compatible = "ti,bq25601-charger",},
{},
};
MODULE_DEVICE_TABLE(of,bq2560x_charger_match_table);
static const struct i2c_device_id bq2560x_charger_id[] = {
{ "bq25600-charger", BQ25600 },
{ "bq25601-charger", BQ25601 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq2560x_charger_id);
static const struct dev_pm_ops bq2560x_pm_ops = {
.resume = bq2560x_resume,
.suspend_noirq = bq2560x_suspend_noirq,
.suspend = bq2560x_suspend,
};
static struct i2c_driver bq2560x_charger_driver = {
.driver = {
.name = "bq2560x-charger",
.owner = THIS_MODULE,
.of_match_table = bq2560x_charger_match_table,
.pm = &bq2560x_pm_ops,
},
.id_table = bq2560x_charger_id,
.probe = bq2560x_charger_probe,
.remove = bq2560x_charger_remove,
.shutdown = bq2560x_charger_shutdown,
};
module_i2c_driver(bq2560x_charger_driver);
MODULE_DESCRIPTION("TI BQ2560x Charger Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Texas Instruments");
#ifndef __BQ2560X_HEADER__
#define __BQ2560X_HEADER__
/* Register 00h */
#define BQ2560X_REG_00 0x00
#define REG00_ENHIZ_MASK 0x80
#define REG00_ENHIZ_SHIFT 7
#define REG00_HIZ_ENABLE 1
#define REG00_HIZ_DISABLE 0
#define REG00_STAT_CTRL_MASK 0x60
#define REG00_STAT_CTRL_SHIFT 5
#define REG00_STAT_CTRL_STAT 0
#define REG00_STAT_CTRL_ICHG 1
#define REG00_STAT_CTRL_IINDPM 2
#define REG00_STAT_CTRL_DISABLE 3
#define REG00_IINLIM_MASK 0x1F
#define REG00_IINLIM_SHIFT 0
#define REG00_IINLIM_LSB 100
#define REG00_IINLIM_BASE 100
/* Register 01h */
#define BQ2560X_REG_01 0x01
#define REG01_PFM_DIS_MASK 0x80
#define REG01_PFM_DIS_SHIFT 7
#define REG01_PFM_ENABLE 0
#define REG01_PFM_DISABLE 1
#define REG01_WDT_RESET_MASK 0x40
#define REG01_WDT_RESET_SHIFT 6
#define REG01_WDT_RESET 1
#define REG01_OTG_CONFIG_MASK 0x20
#define REG01_OTG_CONFIG_SHIFT 5
#define REG01_OTG_ENABLE 1
#define REG01_OTG_DISABLE 0
#define REG01_CHG_CONFIG_MASK 0x10
#define REG01_CHG_CONFIG_SHIFT 4
#define REG01_CHG_DISABLE 0
#define REG01_CHG_ENABLE 1
#define REG01_SYS_MINV_MASK 0x0E
#define REG01_SYS_MINV_SHIFT 1
#define REG01_MIN_VBAT_SEL_MASK 0x01
#define REG01_MIN_VBAT_SEL_SHIFT 0
#define REG01_MIN_VBAT_2P8V 0
#define REG01_MIN_VBAT_2P5V 1
/* Register 0x02*/
#define BQ2560X_REG_02 0x02
#define REG02_BOOST_LIM_MASK 0x80
#define REG02_BOOST_LIM_SHIFT 7
#define REG02_BOOST_LIM_0P5A 0
#define REG02_BOOST_LIM_1P2A 1
#define REG02_Q1_FULLON_MASK 0x40
#define REG02_Q1_FULLON_SHIFT 6
#define REG02_Q1_FULLON_ENABLE 1
#define REG02_Q1_FULLON_DISABLE 0
#define REG02_ICHG_MASK 0x3F
#define REG02_ICHG_SHIFT 0
#define REG02_ICHG_BASE 0
#define REG02_ICHG_LSB 60
/* Register 0x03*/
#define BQ2560X_REG_03 0x03
#define REG03_IPRECHG_MASK 0xF0
#define REG03_IPRECHG_SHIFT 4
#define REG03_IPRECHG_BASE 60
#define REG03_IPRECHG_LSB 60
#define REG03_ITERM_MASK 0x0F
#define REG03_ITERM_SHIFT 0
#define REG03_ITERM_BASE 60
#define REG03_ITERM_LSB 60
/* Register 0x04*/
#define BQ2560X_REG_04 0x04
#define REG04_VREG_MASK 0xF8
#define REG04_VREG_SHIFT 3
#define REG04_VREG_BASE 3856
#define REG04_VREG_LSB 32
#define REG04_TOPOFF_TIMER_MASK 0x06
#define REG04_TOPOFF_TIMER_SHIFT 1
#define REG04_TOPOFF_TIMER_DISABLE 0
#define REG04_TOPOFF_TIMER_15M 1
#define REG04_TOPOFF_TIMER_30M 2
#define REG04_TOPOFF_TIMER_45M 3
#define REG04_VRECHG_MASK 0x01
#define REG04_VRECHG_SHIFT 0
#define REG04_VRECHG_100MV 0
#define REG04_VRECHG_200MV 1
/* Register 0x05*/
#define BQ2560X_REG_05 0x05
#define REG05_EN_TERM_MASK 0x80
#define REG05_EN_TERM_SHIFT 7
#define REG05_TERM_ENABLE 1
#define REG05_TERM_DISABLE 0
#define REG05_WDT_MASK 0x30
#define REG05_WDT_SHIFT 4
#define REG05_WDT_DISABLE 0
#define REG05_WDT_40S 1
#define REG05_WDT_80S 2
#define REG05_WDT_160S 3
#define REG05_WDT_BASE 0
#define REG05_WDT_LSB 40
#define REG05_EN_TIMER_MASK 0x08
#define REG05_EN_TIMER_SHIFT 3
#define REG05_CHG_TIMER_ENABLE 1
#define REG05_CHG_TIMER_DISABLE 0
#define REG05_CHG_TIMER_MASK 0x04
#define REG05_CHG_TIMER_SHIFT 2
#define REG05_CHG_TIMER_5HOURS 0
#define REG05_CHG_TIMER_10HOURS 1
#define REG05_TREG_MASK 0x02
#define REG05_TREG_SHIFT 1
#define REG05_TREG_90C 0
#define REG05_TREG_110C 1
#define REG05_JEITA_ISET_MASK 0x01
#define REG05_JEITA_ISET_SHIFT 0
#define REG05_JEITA_ISET_50PCT 0
#define REG05_JEITA_ISET_20PCT 1
/* Register 0x06*/
#define BQ2560X_REG_06 0x06
#define REG06_OVP_MASK 0xC0
#define REG06_OVP_SHIFT 0x6
#define REG06_OVP_5P5V 0
#define REG06_OVP_6P2V 1
#define REG06_OVP_10P5V 2
#define REG06_OVP_14P3V 3
#define REG06_BOOSTV_MASK 0x30
#define REG06_BOOSTV_SHIFT 4
#define REG06_BOOSTV_4P85V 0
#define REG06_BOOSTV_5V 1
#define REG06_BOOSTV_5P15V 2
#define REG06_BOOSTV_5P3V 3
#define REG06_VINDPM_MASK 0x0F
#define REG06_VINDPM_SHIFT 0
#define REG06_VINDPM_BASE 3900
#define REG06_VINDPM_LSB 100
/* Register 0x07*/
#define BQ2560X_REG_07 0x07
#define REG07_FORCE_DPDM_MASK 0x80
#define REG07_FORCE_DPDM_SHIFT 7
#define REG07_FORCE_DPDM 1
#define REG07_TMR2X_EN_MASK 0x40
#define REG07_TMR2X_EN_SHIFT 6
#define REG07_TMR2X_ENABLE 1
#define REG07_TMR2X_DISABLE 0
#define REG07_BATFET_DIS_MASK 0x20
#define REG07_BATFET_DIS_SHIFT 5
#define REG07_BATFET_OFF 1
#define REG07_BATFET_ON 0
#define REG07_JEITA_VSET_MASK 0x10
#define REG07_JEITA_VSET_SHIFT 4
#define REG07_JEITA_VSET_4100 0
#define REG07_JEITA_VSET_VREG 1
#define REG07_BATFET_DLY_MASK 0x08
#define REG07_BATFET_DLY_SHIFT 3
#define REG07_BATFET_DLY_0S 0
#define REG07_BATFET_DLY_10S 1
#define REG07_BATFET_RST_EN_MASK 0x04
#define REG07_BATFET_RST_EN_SHIFT 2
#define REG07_BATFET_RST_DISABLE 0
#define REG07_BATFET_RST_ENABLE 1
#define REG07_VDPM_BAT_TRACK_MASK 0x03
#define REG07_VDPM_BAT_TRACK_SHIFT 0
#define REG07_VDPM_BAT_TRACK_DISABLE 0
#define REG07_VDPM_BAT_TRACK_200MV 1
#define REG07_VDPM_BAT_TRACK_250MV 2
#define REG07_VDPM_BAT_TRACK_300MV 3
/* Register 0x08*/
#define BQ2560X_REG_08 0x08
#define REG08_VBUS_STAT_MASK 0xE0
#define REG08_VBUS_STAT_SHIFT 5
#define REG08_VBUS_TYPE_NONE 0
#define REG08_VBUS_TYPE_USB 1
#define REG08_VBUS_TYPE_ADAPTER 3
#define REG08_VBUS_TYPE_OTG 7
#define REG08_CHRG_STAT_MASK 0x18
#define REG08_CHRG_STAT_SHIFT 3
#define REG08_CHRG_STAT_IDLE 0
#define REG08_CHRG_STAT_PRECHG 1
#define REG08_CHRG_STAT_FASTCHG 2
#define REG08_CHRG_STAT_CHGDONE 3
#define REG08_PG_STAT_MASK 0x04
#define REG08_PG_STAT_SHIFT 2
#define REG08_POWER_GOOD 1
#define REG08_THERM_STAT_MASK 0x02
#define REG08_THERM_STAT_SHIFT 1
#define REG08_VSYS_STAT_MASK 0x01
#define REG08_VSYS_STAT_SHIFT 0
#define REG08_IN_VSYS_STAT 1
/* Register 0x09*/
#define BQ2560X_REG_09 0x09
#define REG09_FAULT_WDT_MASK 0x80
#define REG09_FAULT_WDT_SHIFT 7
#define REG09_FAULT_WDT 1
#define REG09_FAULT_BOOST_MASK 0x40
#define REG09_FAULT_BOOST_SHIFT 6
#define REG09_FAULT_CHRG_MASK 0x30
#define REG09_FAULT_CHRG_SHIFT 4
#define REG09_FAULT_CHRG_NORMAL 0
#define REG09_FAULT_CHRG_INPUT 1
#define REG09_FAULT_CHRG_THERMAL 2
#define REG09_FAULT_CHRG_TIMER 3
#define REG09_FAULT_BAT_MASK 0x08
#define REG09_FAULT_BAT_SHIFT 3
#define REG09_FAULT_BAT_OVP 1
#define REG09_FAULT_NTC_MASK 0x07
#define REG09_FAULT_NTC_SHIFT 0
#define REG09_FAULT_NTC_NORMAL 0
#define REG09_FAULT_NTC_WARM 2
#define REG09_FAULT_NTC_COOL 3
#define REG09_FAULT_NTC_COLD 5
#define REG09_FAULT_NTC_HOT 6
/* Register 0x0A */
#define BQ2560X_REG_0A 0x0A
#define REG0A_VBUS_GD_MASK 0x80
#define REG0A_VBUS_GD_SHIFT 7
#define REG0A_VBUS_GD 1
#define REG0A_VINDPM_STAT_MASK 0x40
#define REG0A_VINDPM_STAT_SHIFT 6
#define REG0A_VINDPM_ACTIVE 1
#define REG0A_IINDPM_STAT_MASK 0x20
#define REG0A_IINDPM_STAT_SHIFT 5
#define REG0A_IINDPM_ACTIVE 1
#define REG0A_TOPOFF_ACTIVE_MASK 0x08
#define REG0A_TOPOFF_ACTIVE_SHIFT 3
#define REG0A_TOPOFF_ACTIVE 1
#define REG0A_ACOV_STAT_MASK 0x04
#define REG0A_ACOV_STAT_SHIFT 2
#define REG0A_ACOV_ACTIVE 1
#define REG0A_VINDPM_INT_MASK 0x02
#define REG0A_VINDPM_INT_SHIFT 1
#define REG0A_VINDPM_INT_ENABLE 0
#define REG0A_VINDPM_INT_DISABLE 1
#define REG0A_IINDPM_INT_MASK 0x01
#define REG0A_IINDPM_INT_SHIFT 0
#define REG0A_IINDPM_INT_ENABLE 0
#define REG0A_IINDPM_INT_DISABLE 1
#define REG0A_INT_MASK_MASK 0x03
#define REG0A_INT_MASK_SHIFT 0
#define BQ2560X_REG_0B 0x0B
#define REG0B_REG_RESET_MASK 0x80
#define REG0B_REG_RESET_SHIFT 7
#define REG0B_REG_RESET 1
#define REG0B_PN_MASK 0x78
#define REG0B_PN_SHIFT 3
#define REG0B_DEV_REV_MASK 0x03
#define REG0B_DEV_REV_SHIFT 0
#endif
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/version.h>
#include "expander.h"
/*
*-----------------------------------------------------------------------------
* Constants
*-----------------------------------------------------------------------------
*/
#define GENERIC_LED (4)
#define PCM_SEL (5)
#define SDIO_SEL (6)
#define TRI_LED_BLU (7)
#define TRI_LED_GRN (15)
#define TRI_LED_RED (10)
#define BUZZER (0)
struct expander_device {
struct platform_device *pdev;
atomic_t generic_led_val, pcm_sel_val, sdio_sel_val, buzzer_val,
tri_led_blu_val, tri_led_red_val, tri_led_grn_val;
int gpio_expander_base;
};
#define CREATE_SYSFS_DEFN(_var, _offset) \
static ssize_t _var##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct expander_device* exp = dev_get_drvdata(dev); \
return sprintf(buf, "%d\n", atomic_read(&exp->_var##_val)); \
}\
static int _var##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct expander_device* exp = dev_get_drvdata(dev); \
u8 val; \
int ret; \
\
ret = kstrtou8(buf, 10, &val); \
if (ret || val > 1) \
return -EINVAL; \
\
gpio_set_value_cansleep(exp->gpio_expander_base + _offset, val); \
atomic_set(&exp->_var##_val, val); \
\
return count; \
} \
static DEVICE_ATTR_RW(_var)
CREATE_SYSFS_DEFN(generic_led, GENERIC_LED);
CREATE_SYSFS_DEFN(pcm_sel, PCM_SEL);
CREATE_SYSFS_DEFN(sdio_sel, SDIO_SEL);
CREATE_SYSFS_DEFN(tri_led_blu, TRI_LED_BLU);
CREATE_SYSFS_DEFN(tri_led_red, TRI_LED_RED);
CREATE_SYSFS_DEFN(tri_led_grn, TRI_LED_GRN);
CREATE_SYSFS_DEFN(buzzer, BUZZER);
static void gpio_initial_status(struct platform_device *pdev,
struct device_attribute *attr,
int function_number,int function_val,
atomic_t *atomic_val)
{
struct expander_device* exp = dev_get_drvdata(&pdev->dev);
devm_gpio_request(&pdev->dev, exp->gpio_expander_base+function_number,
dev_name(&pdev->dev));
atomic_set(atomic_val, function_val);
gpio_direction_output(exp->gpio_expander_base+function_number,
function_val);
device_create_file(&pdev->dev, attr);
}
static void gpio_final_status(struct platform_device *pdev,
struct device_attribute *attr,
int function_number,int function_val)
{
struct expander_device* exp = dev_get_drvdata(&pdev->dev);
device_remove_file(&pdev->dev, attr);
gpio_set_value_cansleep(exp->gpio_expander_base+function_number, function_val);
}
static int expander_probe(struct platform_device *pdev)
{
struct expander_device* dev;
int ret = 0;
struct expander_platform_data *pdata = dev_get_platdata(&pdev->dev);
dev_info(&pdev->dev, "%s(): probe\n", __func__);
if (!pdata) {
ret = -EINVAL;
dev_err(&pdev->dev, "Required platform data not provided\n");
goto done;
}
/* Create the driver data and remove the allocated memory when driver
is removed */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto done;
}
dev->pdev = pdev;
dev->gpio_expander_base = pdata->gpio_expander_base;
gpio_initial_status(pdev,&dev_attr_generic_led, GENERIC_LED, 0, &dev->generic_led_val);
gpio_initial_status(pdev,&dev_attr_pcm_sel, PCM_SEL, 0, &dev->pcm_sel_val);
gpio_initial_status(pdev,&dev_attr_sdio_sel, SDIO_SEL, 0, &dev->sdio_sel_val);
gpio_initial_status(pdev,&dev_attr_tri_led_blu, TRI_LED_BLU, 0, &dev->tri_led_blu_val);
gpio_initial_status(pdev,&dev_attr_tri_led_red, TRI_LED_RED, 0, &dev->tri_led_red_val);
gpio_initial_status(pdev,&dev_attr_tri_led_grn, TRI_LED_GRN, 0, &dev->tri_led_grn_val);
gpio_initial_status(pdev,&dev_attr_buzzer, BUZZER, 0, &dev->buzzer_val);
platform_set_drvdata(pdev, dev);
done:
return ret;
}
static int expander_remove(struct platform_device *pdev)
{
// struct expander_device* dev = dev_get_drvdata(&pdev->dev);
dev_info(&pdev->dev, "%s(): remove\n", __func__);
/* remove sysfs files & set final state values for gpio expander*/
gpio_final_status(pdev,&dev_attr_generic_led, GENERIC_LED, 0);
gpio_final_status(pdev,&dev_attr_pcm_sel, PCM_SEL, 0);
gpio_final_status(pdev,&dev_attr_sdio_sel, SDIO_SEL, 0);
gpio_final_status(pdev,&dev_attr_tri_led_blu, TRI_LED_BLU, 0);
gpio_final_status(pdev,&dev_attr_tri_led_red, TRI_LED_RED, 0);
gpio_final_status(pdev,&dev_attr_tri_led_grn, TRI_LED_GRN, 0);
gpio_final_status(pdev,&dev_attr_buzzer, BUZZER, 0);
return 0;
}
static const struct platform_device_id yellow_expander_ids[] = {
{"expander", (kernel_ulong_t)0},
{},
};
MODULE_DEVICE_TABLE(platform, yellow_expander_ids);
static struct platform_driver expander_driver = {
.probe = expander_probe,
.remove = expander_remove,
.driver = {
.name = "expander",
.owner = THIS_MODULE,
.bus = &platform_bus_type,
},
.id_table = yellow_expander_ids,
};
static int __init expander_init(void)
{
platform_driver_register(&expander_driver);
return 0;
}
static void __exit expander_exit(void)
{
platform_driver_unregister(&expander_driver);
}
module_init(expander_init);
module_exit(expander_exit);
MODULE_ALIAS("platform:expander");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sierra Wireless");
MODULE_DESCRIPTION("EXPANDER driver");
MODULE_VERSION("0.1");
#ifndef EXPANDER_H
#define EXPANDER_H
struct expander_platform_data {
int gpio_expander_base;
};
#endif /* EXPANDER_H */
sources:
{
expander.c
}
cflags:
{
// -DDEBUG
}
params:
{
}
......@@ -5,6 +5,14 @@
/* TODO: There should be a better way to convert from WP GPIO numbers to real GPIO numbers */
#if defined(CONFIG_ARCH_MSM9615) /* For WPX5XX */
//#include "/opt/swi/y22-ext-SWI9X15Y_07.13.05.00/sysroots/armv7a-neon-poky-linux-gnueabi/usr/src/kernel/arch/arm/mach-msm/board-9615.h"
#include <../arch/arm/mach-msm/board-9615.h>
#elif defined(CONFIG_ARCH_MDM9607) /* For WP76XX */
#include <../arch/arm/mach-msm/include/mach/swimcu.h>
#endif
#if defined(CONFIG_ARCH_MSM9615)
#define PRIMARY_I2C_BUS (0)
#define PRIMARY_SPI_BUS (0)
......@@ -14,7 +22,10 @@
#define CF3_GPIO8 (29)
#define CF3_GPIO2 (59)
#define CF3_GPIO33 (78)
#define CF3_GPIO36 SWIMCU_GPIO_TO_SYS(2)
#elif defined(CONFIG_ARCH_MDM9607) /* For WP76XX */
#define PRIMARY_I2C_BUS (4)
#define PRIMARY_SPI_BUS (1)
......@@ -24,6 +35,7 @@
#define CF3_GPIO8 (58)
#define CF3_GPIO2 (38)
#define CF3_GPIO33 (78)
#define CF3_GPIO36 SWIMCU_GPIO_TO_SYS(2)
#endif
#endif /* MANGOH_COMMON_H */
......@@ -4,8 +4,17 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio/driver.h>
#include <linux/platform_data/at24.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include "mangoh_common.h"
#include "expander.h"
//#include "bq25601-platform-data.h"
//#include "bq27426-platform-data.h"
#include <linux/i2c/pca954x.h>
#include <linux/i2c/sx150x.h>
/*
*-----------------------------------------------------------------------------
......@@ -13,11 +22,10 @@
*-----------------------------------------------------------------------------
*/
#define MANGOH_YELLOW_I2C_SW_BUS_BASE (PRIMARY_I2C_BUS + 1)
#define MANGOH_YELLOW_I2C_BUS_IOT0 (MANGOH_YELLOW_I2C_SW_BUS_BASE + 0)
#define MANGOH_YELLOW_I2C_BUS_BATTERY_CHARGER (MANGOH_YELLOW_I2C_SW_BUS_BASE + 1)
#define MANGOH_YELLOW_I2C_BUS_USB_HUB (MANGOH_YELLOW_I2C_SW_BUS_BASE + 1)
#define MANGOH_YELLOW_I2C_BUS_GPIO_EXPANDER (MANGOH_YELLOW_I2C_SW_BUS_BASE + 2)
#define MANGOH_YELLOW_I2C_BUS_EXP (MANGOH_YELLOW_I2C_SW_BUS_BASE + 3)
#define MANGOH_YELLOW_I2C_BUS_PORT0 (MANGOH_YELLOW_I2C_SW_BUS_BASE + 0)
#define MANGOH_YELLOW_I2C_BUS_PORT1 (MANGOH_YELLOW_I2C_SW_BUS_BASE + 1)
#define MANGOH_YELLOW_I2C_BUS_PORT2 (MANGOH_YELLOW_I2C_SW_BUS_BASE + 2)
#define MANGOH_YELLOW_I2C_BUS_PORT3 (MANGOH_YELLOW_I2C_SW_BUS_BASE + 3)
/*
*-----------------------------------------------------------------------------
......@@ -25,7 +33,8 @@
*-----------------------------------------------------------------------------
*/
enum mangoh_yellow_board_rev {
MANGOH_YELLOW_BOARD_REV_DV1,
MANGOH_YELLOW_BOARD_REV_PROD,
MANGOH_YELLOW_BOARD_REV_DEV,
};
/*
......@@ -36,6 +45,7 @@ enum mangoh_yellow_board_rev {
static void mangoh_yellow_release(struct device* dev);
static int mangoh_yellow_probe(struct platform_device* pdev);
static int mangoh_yellow_remove(struct platform_device* pdev);
static void mangoh_yellow_expander_release(struct device *dev);
/*
*-----------------------------------------------------------------------------
......@@ -43,7 +53,9 @@ static int mangoh_yellow_remove(struct platform_device* pdev);
*-----------------------------------------------------------------------------
*/
static char *revision = "dv1";
static char *revision_dev = "dev";
static char *revision_prod = "prod";
static char *revision = "dev";
module_param(revision, charp, S_IRUGO);
MODULE_PARM_DESC(revision, "mangOH Yellow board revision");
......@@ -62,10 +74,19 @@ static struct mangoh_yellow_platform_data {
} mangoh_yellow_pdata;
static struct mangoh_yellow_driver_data {
struct i2c_client* bme680;
struct i2c_client* bmi088a;
struct i2c_client* bmi088g;
struct i2c_client* eeprom;
struct i2c_client* i2c_switch;
struct i2c_client* imu;
struct i2c_client* environmental;
struct i2c_client* rtc;
struct i2c_client* light;
struct i2c_client* magnetometer;
struct i2c_client* battery_gauge;
struct i2c_client* battery_charger;
struct i2c_client* gpio_expander;
bool expander_registered;
} mangoh_yellow_driver_data = {
.expander_registered = false,
};
static struct platform_device mangoh_yellow_device = {
......@@ -77,16 +98,81 @@ static struct platform_device mangoh_yellow_device = {
},
};
static struct i2c_board_info mangoh_yellow_bme680_devinfo = {
I2C_BOARD_INFO("bme680", 0x76),
static struct pca954x_platform_mode mangoh_yellow_pca954x_adap_modes[] = {
{.adap_id=MANGOH_YELLOW_I2C_SW_BUS_BASE + 0, .deselect_on_exit=1, .class=0},
{.adap_id=MANGOH_YELLOW_I2C_SW_BUS_BASE + 1, .deselect_on_exit=1, .class=0},
{.adap_id=MANGOH_YELLOW_I2C_SW_BUS_BASE + 2, .deselect_on_exit=1, .class=0},
{.adap_id=MANGOH_YELLOW_I2C_SW_BUS_BASE + 3, .deselect_on_exit=1, .class=0},
};
static struct i2c_board_info mangoh_yellow_bmi088a_devinfo = {
I2C_BOARD_INFO("bmi088a", 0x18),
static struct pca954x_platform_data mangoh_yellow_pca954x_platform_data = {
mangoh_yellow_pca954x_adap_modes,
ARRAY_SIZE(mangoh_yellow_pca954x_adap_modes),
};
static const struct i2c_board_info mangoh_yellow_pca954x_device_info = {
I2C_BOARD_INFO("pca9546",0x71),
.platform_data = &mangoh_yellow_pca954x_platform_data,
};
static struct i2c_board_info mangoh_yellow_bmi088g_devinfo = {
I2C_BOARD_INFO("bmi088g", 0x68),
static struct sx150x_platform_data mangoh_yellow_gpio_expander_platform_data = {
.gpio_base = -1,
.oscio_is_gpo = false,
.io_pullup_ena = 0,
.io_pulldn_ena = 0,
.io_open_drain_ena = 0,
.io_polarity = 0,
.irq_summary = -1,
.irq_base = -1,
.reset_during_probe = true,
};
static const struct i2c_board_info mangoh_yellow_gpio_expander_devinfo = {
I2C_BOARD_INFO("sx1509q",0x3e),
.platform_data = &mangoh_yellow_gpio_expander_platform_data,
.irq = 0,
};
static struct i2c_board_info mangoh_yellow_imu_devinfo = {
I2C_BOARD_INFO("bmi160", 0x68),
};
static struct i2c_board_info mangoh_yellow_environmental_devinfo = {
I2C_BOARD_INFO("bme680", 0x76),
};
static struct i2c_board_info mangoh_yellow_rtc_devinfo = {
I2C_BOARD_INFO("pcf85063", 0x51),
};
static struct i2c_board_info mangoh_yellow_battery_charger_devinfo = {
I2C_BOARD_INFO("bq25601", 0x6b),
};
static struct i2c_board_info mangoh_yellow_battery_gauge_devinfo = {
I2C_BOARD_INFO("bq27426", 0x55),
};
static struct i2c_board_info mangoh_yellow_magnetometer_devinfo = {
I2C_BOARD_INFO("bmm150", 0x10),
};
static struct i2c_board_info mangoh_yellow_light_devinfo = {
I2C_BOARD_INFO("opt3002", 0x44),
};
static struct expander_platform_data mangoh_yellow_expander_platform_data = {
.gpio_expander_base = -1,
};
static struct platform_device mangoh_yellow_expander = {
.name = "expander",
.dev = {
.platform_data = &mangoh_yellow_expander_platform_data,
.release = mangoh_yellow_expander_release,
},
};
/*
* The EEPROM is marked as read-only to prevent accidental writes. The mangOH
* Yellow has the write protect (WP) pin pulled high which has the effect of making
* the upper 1/4 of the address space of the EEPROM write protected by hardware.
*/
static struct at24_platform_data mangoh_yellow_eeprom_data = {
.byte_len = 4096,
.page_size = 32,
.flags = (AT24_FLAG_ADDR16 | AT24_FLAG_READONLY),
};
static struct i2c_board_info mangoh_yellow_eeprom_info = {
I2C_BOARD_INFO("at24", 0x51),
.platform_data = &mangoh_yellow_eeprom_data,
};
static void mangoh_yellow_release(struct device* dev)
......@@ -97,7 +183,9 @@ static void mangoh_yellow_release(struct device* dev)
static int mangoh_yellow_probe(struct platform_device* pdev)
{
int ret = 0;
struct i2c_adapter *i2c_adapter_primary;
struct i2c_adapter *i2c_adapter_primary, *i2c_adapter_port1 = NULL,
*i2c_adapter_port2 = NULL, *i2c_adapter_port3 = NULL;
struct gpio_chip *gpio_expander;
dev_info(&pdev->dev, "%s(): probe\n", __func__);
......@@ -109,43 +197,154 @@ static int mangoh_yellow_probe(struct platform_device* pdev)
goto done;
}
/*
* This is a workaround of questionable validity for USB issues first
* seen on the mangOH Green.
/* This is a workaround that needs to be tested more for issue first seen on
* mangOH Green
*/
msleep(5000);
platform_set_drvdata(pdev, &mangoh_yellow_driver_data);
/* Map the I2C BME680 humidity/gas/temp/pressure sensor */
/* map the EEPROM */
dev_dbg(&pdev->dev, "mapping eeprom\n");
mangoh_yellow_driver_data.eeprom =
i2c_new_device(i2c_adapter_primary, &mangoh_yellow_eeprom_info);
if (!mangoh_yellow_driver_data.eeprom) {
dev_err(&pdev->dev, "Failed to register %s\n",
mangoh_yellow_eeprom_info.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C switch */
dev_dbg(&pdev->dev, "mapping i2c switch\n");
mangoh_yellow_driver_data.i2c_switch =
i2c_new_device(i2c_adapter_primary, &mangoh_yellow_pca954x_device_info);
if (!mangoh_yellow_driver_data.i2c_switch) {
dev_err(&pdev->dev, "Failed to register %s\n",
mangoh_yellow_pca954x_device_info.type);
ret = -ENODEV;
goto cleanup;
}
i2c_adapter_port1 = i2c_get_adapter(MANGOH_YELLOW_I2C_BUS_PORT1);
i2c_adapter_port2 = i2c_get_adapter(MANGOH_YELLOW_I2C_BUS_PORT2);
i2c_adapter_port3 = i2c_get_adapter(MANGOH_YELLOW_I2C_BUS_PORT3);
if (!i2c_adapter_port1 || !i2c_adapter_port2 || !i2c_adapter_port3) {
dev_err(&pdev->dev,
"Couldn't get necessary I2C buses downstream of I2C switch\n");
ret = -ENODEV;
goto cleanup;
}
/* Map the GPIO expander */
dev_dbg(&pdev->dev, "mapping the gpio expander\n");
mangoh_yellow_driver_data.gpio_expander =
i2c_new_device(i2c_adapter_port3, &mangoh_yellow_gpio_expander_devinfo);
if (!mangoh_yellow_driver_data.gpio_expander) {
dev_err(&pdev->dev, "Failed to register %s\n",
mangoh_yellow_gpio_expander_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
gpio_expander = i2c_get_clientdata(mangoh_yellow_driver_data.gpio_expander);
/* Map the Expander gpios as hardcoded functions */
mangoh_yellow_expander_platform_data.gpio_expander_base = gpio_expander->base;
ret = platform_device_register(&mangoh_yellow_expander);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register expander gpios for hardcoding\n");
goto cleanup;
}
mangoh_yellow_driver_data.expander_registered = true;
/* Map the I2C BME680 environmental sensor for gas/humidity/temp/pressure sensor */
dev_dbg(&pdev->dev, "mapping bme680 gas/temperature/pressure sensor\n");
mangoh_yellow_driver_data.bme680 =
i2c_new_device(i2c_adapter_primary, &mangoh_yellow_bme680_devinfo);
if (!mangoh_yellow_driver_data.bme680) {
dev_err(&pdev->dev, "Gas/Humidity/Temp sensor is missing\n");
mangoh_yellow_driver_data.environmental =
i2c_new_device(i2c_adapter_port1, &mangoh_yellow_environmental_devinfo);
if (!mangoh_yellow_driver_data.environmental) {
dev_err(&pdev->dev, "Failed to register environmental sensor %s\n",
mangoh_yellow_environmental_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C BMI088 gyro/accel sensor */
dev_dbg(&pdev->dev, "mapping bmi088 gyro/accel sensor\n");
mangoh_yellow_driver_data.bmi088a =
i2c_new_device(i2c_adapter_primary, &mangoh_yellow_bmi088a_devinfo);
if (!mangoh_yellow_driver_data.bmi088a) {
dev_err(&pdev->dev, "bmi088 accel sensor is missing\n");
/* Map the I2C BMI160 IMU for gyro/accel/temp sensor */
dev_dbg(&pdev->dev, "mapping bmi160 gyro/accel/temp sensor\n");
mangoh_yellow_driver_data.imu =
i2c_new_device(i2c_adapter_port1, &mangoh_yellow_imu_devinfo);
if (!mangoh_yellow_driver_data.imu) {
dev_err(&pdev->dev, "BMI160 IMU accel/gyro/temp sensor is missing %s\n",
mangoh_yellow_imu_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C BMM150 magnetometer*/
dev_dbg(&pdev->dev, "mapping bmm150 magnetometer sensor\n");
mangoh_yellow_driver_data.magnetometer =
i2c_new_device(i2c_adapter_port1, &mangoh_yellow_magnetometer_devinfo);
if (!mangoh_yellow_driver_data.imu) {
dev_err(&pdev->dev, "BMM150 sensor is missing %s\n",
mangoh_yellow_magnetometer_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C RTC */
dev_dbg(&pdev->dev, "mapping RTC\n");
mangoh_yellow_driver_data.rtc =
i2c_new_device(i2c_adapter_port3, &mangoh_yellow_rtc_devinfo);
if (!mangoh_yellow_driver_data.rtc) {
dev_err(&pdev->dev, "RTC is missing %s\n",
mangoh_yellow_rtc_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
mangoh_yellow_driver_data.bmi088g =
i2c_new_device(i2c_adapter_primary, &mangoh_yellow_bmi088g_devinfo);
if (!mangoh_yellow_driver_data.bmi088g) {
dev_err(&pdev->dev, "bmi088 gyro sensor is missing\n");
/* Map the I2C Battery Charger*/
dev_dbg(&pdev->dev, "mapping battery charger\n");
mangoh_yellow_driver_data.battery_charger =
i2c_new_device(i2c_adapter_port2, &mangoh_yellow_battery_charger_devinfo);
if (!mangoh_yellow_driver_data.battery_charger) {
dev_err(&pdev->dev, "Battery Charger is missing %s\n",
mangoh_yellow_battery_charger_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C Battery Gauge*/
dev_dbg(&pdev->dev, "mapping battery gauge\n");
mangoh_yellow_driver_data.battery_charger =
i2c_new_device(i2c_adapter_port2, &mangoh_yellow_battery_gauge_devinfo);
if (!mangoh_yellow_driver_data.battery_gauge) {
dev_err(&pdev->dev, "Battery Gauge is missing %s\n",
mangoh_yellow_battery_charger_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
/* Map the I2C Light Sensor */
dev_dbg(&pdev->dev, "mapping Light Sensor\n");
mangoh_yellow_light_devinfo.irq = gpio_to_irq(CF3_GPIO36);
mangoh_yellow_driver_data.light =
i2c_new_device(i2c_adapter_port2, &mangoh_yellow_light_devinfo);
if (!mangoh_yellow_driver_data.light) {
dev_err(&pdev->dev, "Light Sensor is missing %s\n",
mangoh_yellow_light_devinfo.type);
ret = -ENODEV;
goto cleanup;
}
cleanup:
i2c_put_adapter(i2c_adapter_port3);
i2c_put_adapter(i2c_adapter_port2);
i2c_put_adapter(i2c_adapter_port1);
i2c_put_adapter(i2c_adapter_primary);
if (ret != 0)
mangoh_yellow_remove(pdev);
......@@ -157,21 +356,42 @@ static int mangoh_yellow_remove(struct platform_device* pdev)
{
struct mangoh_yellow_driver_data *dd = platform_get_drvdata(pdev);
dev_info(&pdev->dev, "Removing mangoh red platform device\n");
i2c_unregister_device(dd->bmi088a);
i2c_unregister_device(dd->bmi088g);
i2c_unregister_device(dd->bme680);
dev_info(&pdev->dev, "Removing mangoh yellow platform device\n");
i2c_unregister_device(dd->environmental);
i2c_unregister_device(dd->imu);
i2c_unregister_device(dd->magnetometer);
i2c_unregister_device(dd->light);
i2c_unregister_device(dd->battery_charger);
i2c_unregister_device(dd->battery_gauge);
i2c_unregister_device(dd->rtc);
if (dd->expander_registered)
platform_device_unregister(&mangoh_yellow_expander);
i2c_unregister_device(dd->gpio_expander);
i2c_unregister_device(dd->i2c_switch);
i2c_unregister_device(dd->eeprom);
return 0;
}
static void mangoh_yellow_expander_release(struct device *dev)
{
/* do nothing*/
}
static int __init mangoh_yellow_init(void)
{
platform_driver_register(&mangoh_yellow_driver);
printk(KERN_DEBUG "mangoh: registered platform driver\n");
mangoh_yellow_pdata.board_rev = MANGOH_YELLOW_BOARD_REV_DV1;
if ( strcmp(revision, revision_prod) == 0) {
mangoh_yellow_pdata.board_rev = MANGOH_YELLOW_BOARD_REV_PROD;
} else if (strcmp(revision, revision_dev) == 0) {
mangoh_yellow_pdata.board_rev = MANGOH_YELLOW_BOARD_REV_DEV;
} else {
pr_err("%s:Unsupported mangoh yellow board revision (%s)\n",
__func__, revision);
return -ENODEV;
}
if (platform_device_register(&mangoh_yellow_device)) {
platform_driver_unregister(&mangoh_yellow_driver);
......
cflags:
{
-I${MANGOH_ROOT}/linux_kernel_modules/expander
-I${MANGOH_ROOT}/linux_kernel_modules/opt300x
}
sources:
......@@ -10,7 +11,7 @@ sources:
params:
{
revision = "dv1"
revision = "dev"
}
......@@ -18,7 +19,14 @@ requires:
{
kernelModules:
{
$CURDIR/../bme680/bme680-i2c
$CURDIR/../bmi088/bmi088-i2c
$CURDIR/../bmi160/bmi160-i2c
// $CURDIR/../bme680/bme680-i2c
$CURDIR/../rtc-pcf85063/rtc-pcf85063
$CURDIR/../bq25601/bq25601
// $CURDIR/../bq27426/bq27426-i2c
// $CURDIR/../bmm150/bmm150-i2c
$CURDIR/../opt300x/opt300x
$CURDIR/../expander/expander
}
}
#
# Light sensors
#
# When adding new entries keep the list in alphabetical order
menu "Light sensors"
config OPT3001
tristate "Texas Instruments OPT3001 Light Sensor"
depends on I2C
help
If you say Y or M here, you get support for Texas Instruments
OPT3001 Ambient Light Sensor.
If built as a dynamically linked module, it will be called
opt3001.
endmenu
Driver files were copied from https://github.com/torvalds/linux/tree/master/drivers/iio/light in revision 4.18.10
/**
* opt300x.c - Texas Instruments OPT300x Light Sensor
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Andreas Dannenberg <dannenberg@ti.com>
* Based on previous work from: Felipe Balbi <balbi@ti.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 of the License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define OPT300x_RESULT 0x00
#define OPT300x_CONFIGURATION 0x01
#define OPT300x_LOW_LIMIT 0x02
#define OPT300x_HIGH_LIMIT 0x03
#define OPT300x_MANUFACTURER_ID 0x7e
#define OPT300x_DEVICE_ID 0x7f
#define MANUFACTURER_ID_TI 0x5449
#define OPT300x_CONFIGURATION_RN_MASK (0xf << 12)
#define OPT300x_CONFIGURATION_RN_AUTO (0xc << 12)
#define OPT300x_CONFIGURATION_CT BIT(11)
#define OPT300x_CONFIGURATION_M_MASK (3 << 9)
#define OPT300x_CONFIGURATION_M_SHUTDOWN (0 << 9)
#define OPT300x_CONFIGURATION_M_SINGLE (1 << 9)
#define OPT300x_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */
#define OPT300x_CONFIGURATION_OVF BIT(8)
#define OPT300x_CONFIGURATION_CRF BIT(7)
#define OPT300x_CONFIGURATION_FH BIT(6)
#define OPT300x_CONFIGURATION_FL BIT(5)
#define OPT300x_CONFIGURATION_L BIT(4)
#define OPT300x_CONFIGURATION_POL BIT(3)
#define OPT300x_CONFIGURATION_ME BIT(2)
#define OPT300x_CONFIGURATION_FC_MASK (3 << 0)
/* The end-of-conversion enable is located in the low-limit register */
#define OPT300x_LOW_LIMIT_EOC_ENABLE 0xc000
#define OPT300x_REG_EXPONENT(n) ((n) >> 12)
#define OPT300x_REG_MANTISSA(n) ((n) & 0xfff)
#define OPT300x_INT_TIME_LONG 800000
#define OPT300x_INT_TIME_SHORT 100000
/*
* Time to wait for conversion result to be ready. The device datasheet
* sect. 6.5 states results are ready after total integration time plus 3ms.
* This results in worst-case max values of 113ms or 883ms, respectively.
* Add some slack to be on the safe side.
*/
#define OPT300x_RESULT_READY_SHORT 150
#define OPT300x_RESULT_READY_LONG 1000
struct opt300x_scale {
int val;
int val2;
};
static const struct opt300x_scale opt3001_scales[] = {
{
.val = 40,
.val2 = 950000,
},
{
.val = 81,
.val2 = 900000,
},
{
.val = 163,
.val2 = 800000,
},
{
.val = 327,
.val2 = 600000,
},
{
.val = 655,
.val2 = 200000,
},
{
.val = 1310,
.val2 = 400000,
},
{
.val = 2620,
.val2 = 800000,
},
{
.val = 5241,
.val2 = 600000,
},
{
.val = 10483,
.val2 = 200000,
},
{
.val = 20966,
.val2 = 400000,
},
{
.val = 41932,
.val2 = 800000,
},
{
.val = 83865,
.val2 = 600000,
},
};
static const struct opt300x_scale opt3002_scales[] = {
{
.val = 4914,
.val2 = 0,
},
{
.val = 9828,
.val2 = 0,
},
{
.val = 19656,
.val2 =0,
},
{
.val = 39312,
.val2 = 0,
},
{
.val = 78624,
.val2 = 0,
},
{
.val = 157248,
.val2 = 0,
},
{
.val = 314496,
.val2 = 0,
},
{
.val = 628992,
.val2 = 0,
},
{
.val = 1257984,
.val2 = 0,
},
{
.val = 2515968,
.val2 = 0,
},
{
.val = 5031936,
.val2 = 0,
},
{
.val = 10063872,
.val2 = 0,
},
};
struct opt300x_chip {
int device_id;
u16 multiplier_numerator;
u16 multiplier_denominator;
const struct opt300x_scale *table;
size_t table_num_entries;
};
static const struct opt300x_chip opt3001_chip = {
.device_id = 0x3001,
.multiplier_numerator = 1,
.multiplier_denominator = 100,
.table = opt3001_scales,
.table_num_entries = ARRAY_SIZE(opt3001_scales),
};
static const struct opt300x_chip opt3002_chip = {
.device_id = -1,
.multiplier_numerator = 6,
.multiplier_denominator = 5,
.table = opt3001_scales,
.table_num_entries = ARRAY_SIZE(opt3002_scales),
};
struct opt300x {
struct i2c_client *client;
struct device *dev;
struct opt300x_chip *chip;
struct mutex lock;
bool ok_to_ignore_lock;
bool result_ready;
wait_queue_head_t result_ready_queue;
u16 result;
u32 int_time;
u32 mode;
u16 high_thresh_mantissa;
u16 low_thresh_mantissa;
u8 high_thresh_exp;
u8 low_thresh_exp;
bool use_irq;
};
static int opt300x_find_scale(const struct opt300x *opt, int val,
int val2, u8 *exponent)
{
int i;
for (i = 0; i < opt->chip->table_num_entries; i++) {
const struct opt300x_scale *scale = &opt->chip->table[i];
/*
* Combine the integer and micro parts for comparison
* purposes. Use milli lux precision to avoid 32-bit integer
* overflows.
*/
if ((val * 1000 + val2 / 1000) <=
(scale->val * 1000 + scale->val2 / 1000)) {
*exponent = i;
return 0;
}
}
return -EINVAL;
}
static void opt300x_to_iio_ret(struct opt300x *opt, u8 exponent,
u16 mantissa, int *val, int *val2)
{
int lux;
u16 multiplier1 = opt->chip->multiplier_numerator;
u16 multiplier2 = opt->chip->multiplier_denominator;
lux = (1000000/multiplier2)* (mantissa << exponent)*multiplier1;
*val = lux /1000000;
*val2 = lux - (1000000*(*val));
}
static void opt300x_set_mode(struct opt300x *opt, u16 *reg, u16 mode)
{
*reg &= ~OPT300x_CONFIGURATION_M_MASK;
*reg |= mode;
opt->mode = mode;
}
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.8");
static struct attribute *opt300x_attributes[] = {
&iio_const_attr_integration_time_available.dev_attr.attr,
NULL
};
static const struct attribute_group opt300x_attribute_group = {
.attrs = opt300x_attributes,
};
static const struct iio_event_spec opt300x_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec opt300x_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_INT_TIME),
.event_spec = opt300x_event_spec,
.num_event_specs = ARRAY_SIZE(opt300x_event_spec),
},
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static int opt300x_get_lux(struct opt300x *opt, int *val, int *val2)
{
int ret;
u16 mantissa;
u16 reg;
u8 exponent;
u16 value;
long timeout;
if (opt->use_irq) {
/*
* Enable the end-of-conversion interrupt mechanism. Note that
* doing so will overwrite the low-level limit value however we
* will restore this value later on.
*/
ret = i2c_smbus_write_word_swapped(opt->client,
OPT300x_LOW_LIMIT,
OPT300x_LOW_LIMIT_EOC_ENABLE);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_LOW_LIMIT);
return ret;
}
/* Allow IRQ to access the device despite lock being set */
opt->ok_to_ignore_lock = true;
}
/* Reset data-ready indicator flag */
opt->result_ready = false;
/* Configure for single-conversion mode and start a new conversion */
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
goto err;
}
reg = ret;
opt300x_set_mode(opt, &reg, OPT300x_CONFIGURATION_M_SINGLE);
ret = i2c_smbus_write_word_swapped(opt->client, OPT300x_CONFIGURATION,
reg);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_CONFIGURATION);
goto err;
}
if (opt->use_irq) {
/* Wait for the IRQ to indicate the conversion is complete */
ret = wait_event_timeout(opt->result_ready_queue,
opt->result_ready,
msecs_to_jiffies(OPT300x_RESULT_READY_LONG));
} else {
/* Sleep for result ready time */
timeout = (opt->int_time == OPT300x_INT_TIME_SHORT) ?
OPT300x_RESULT_READY_SHORT : OPT300x_RESULT_READY_LONG;
msleep(timeout);
/* Check result ready flag */
ret = i2c_smbus_read_word_swapped(opt->client,
OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
goto err;
}
if (!(ret & OPT300x_CONFIGURATION_CRF)) {
ret = -ETIMEDOUT;
goto err;
}
/* Obtain value */
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_RESULT);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_RESULT);
goto err;
}
opt->result = ret;
opt->result_ready = true;
}
err:
if (opt->use_irq)
/* Disallow IRQ to access the device while lock is active */
opt->ok_to_ignore_lock = false;
if (ret == 0)
return -ETIMEDOUT;
else if (ret < 0)
return ret;
if (opt->use_irq) {
/*
* Disable the end-of-conversion interrupt mechanism by
* restoring the low-level limit value (clearing
* OPT300x_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
* those enable bits would affect the actual limit value due to
* bit-overlap and therefore can't be done.
*/
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
ret = i2c_smbus_write_word_swapped(opt->client,
OPT300x_LOW_LIMIT,
value);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_LOW_LIMIT);
return ret;
}
}
exponent = OPT300x_REG_EXPONENT(opt->result);
mantissa = OPT300x_REG_MANTISSA(opt->result);
opt300x_to_iio_ret(opt, exponent, mantissa, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
}
static int opt300x_get_int_time(struct opt300x *opt, int *val, int *val2)
{
*val = 0;
*val2 = opt->int_time;
return IIO_VAL_INT_PLUS_MICRO;
}
static int opt300x_set_int_time(struct opt300x *opt, int time)
{
int ret;
u16 reg;
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
return ret;
}
reg = ret;
switch (time) {
case OPT300x_INT_TIME_SHORT:
reg &= ~OPT300x_CONFIGURATION_CT;
opt->int_time = OPT300x_INT_TIME_SHORT;
break;
case OPT300x_INT_TIME_LONG:
reg |= OPT300x_CONFIGURATION_CT;
opt->int_time = OPT300x_INT_TIME_LONG;
break;
default:
return -EINVAL;
}
return i2c_smbus_write_word_swapped(opt->client, OPT300x_CONFIGURATION,
reg);
}
static int opt300x_read_raw(struct iio_dev *iio,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct opt300x *opt = iio_priv(iio);
int ret;
if (opt->mode == OPT300x_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
if (chan->type != IIO_LIGHT)
return -EINVAL;
mutex_lock(&opt->lock);
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
ret = opt300x_get_lux(opt, val, val2);
break;
case IIO_CHAN_INFO_INT_TIME:
ret = opt300x_get_int_time(opt, val, val2);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&opt->lock);
return ret;
}
static int opt300x_write_raw(struct iio_dev *iio,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct opt300x *opt = iio_priv(iio);
int ret;
if (opt->mode == OPT300x_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
if (chan->type != IIO_LIGHT)
return -EINVAL;
if (mask != IIO_CHAN_INFO_INT_TIME)
return -EINVAL;
if (val != 0)
return -EINVAL;
mutex_lock(&opt->lock);
ret = opt300x_set_int_time(opt, val2);
mutex_unlock(&opt->lock);
return ret;
}
static int opt300x_read_event_value(struct iio_dev *iio,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info,
int *val, int *val2)
{
struct opt300x *opt = iio_priv(iio);
int ret = IIO_VAL_INT_PLUS_MICRO;
mutex_lock(&opt->lock);
switch (dir) {
case IIO_EV_DIR_RISING:
opt300x_to_iio_ret(opt, opt->high_thresh_exp,
opt->high_thresh_mantissa, val, val2);
break;
case IIO_EV_DIR_FALLING:
opt300x_to_iio_ret(opt, opt->low_thresh_exp,
opt->low_thresh_mantissa, val, val2);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&opt->lock);
return ret;
}
static int opt300x_write_event_value(struct iio_dev *iio,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info,
int val, int val2)
{
struct opt300x *opt = iio_priv(iio);
int ret;
u16 mantissa;
u16 value;
u16 reg;
u8 exponent;
if (val < 0)
return -EINVAL;
mutex_lock(&opt->lock);
ret = opt300x_find_scale(opt, val, val2, &exponent);
if (ret < 0) {
dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2);
goto err;
}
mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
value = (exponent << 12) | mantissa;
switch (dir) {
case IIO_EV_DIR_RISING:
reg = OPT300x_HIGH_LIMIT;
opt->high_thresh_mantissa = mantissa;
opt->high_thresh_exp = exponent;
break;
case IIO_EV_DIR_FALLING:
reg = OPT300x_LOW_LIMIT;
opt->low_thresh_mantissa = mantissa;
opt->low_thresh_exp = exponent;
break;
default:
ret = -EINVAL;
goto err;
}
ret = i2c_smbus_write_word_swapped(opt->client, reg, value);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n", reg);
goto err;
}
err:
mutex_unlock(&opt->lock);
return ret;
}
static int opt300x_read_event_config(struct iio_dev *iio,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir)
{
struct opt300x *opt = iio_priv(iio);
return opt->mode == OPT300x_CONFIGURATION_M_CONTINUOUS;
}
static int opt300x_write_event_config(struct iio_dev *iio,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, int state)
{
struct opt300x *opt = iio_priv(iio);
int ret;
u16 mode;
u16 reg;
if (state && opt->mode == OPT300x_CONFIGURATION_M_CONTINUOUS)
return 0;
if (!state && opt->mode == OPT300x_CONFIGURATION_M_SHUTDOWN)
return 0;
mutex_lock(&opt->lock);
mode = state ? OPT300x_CONFIGURATION_M_CONTINUOUS
: OPT300x_CONFIGURATION_M_SHUTDOWN;
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
goto err;
}
reg = ret;
opt300x_set_mode(opt, &reg, mode);
ret = i2c_smbus_write_word_swapped(opt->client, OPT300x_CONFIGURATION,
reg);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_CONFIGURATION);
goto err;
}
err:
mutex_unlock(&opt->lock);
return ret;
}
static const struct iio_info opt300x_info = {
.attrs = &opt300x_attribute_group,
.read_raw = opt300x_read_raw,
.write_raw = opt300x_write_raw,
.read_event_value = opt300x_read_event_value,
.write_event_value = opt300x_write_event_value,
.read_event_config = opt300x_read_event_config,
.write_event_config = opt300x_write_event_config,
};
static int opt300x_read_id(struct opt300x *opt)
{
u16 manufacturer;
u16 device_id;
int ret;
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_MANUFACTURER_ID);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_MANUFACTURER_ID);
return ret;
}
manufacturer = ret;
if (manufacturer != MANUFACTURER_ID_TI) {
dev_err(opt->dev, "Invalid manufacturer ID (%u)\n",
manufacturer);
return -ENODEV;
}
if (opt->chip->device_id >= 0) {
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_DEVICE_ID);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_DEVICE_ID);
return ret;
}
device_id = ret;
if (device_id != opt->chip->device_id) {
//dev_err("opt->dev, Invalid Device id %d", device_id);
return -ENODEV;
}
}
return 0;
}
static int opt300x_configure(struct opt300x *opt)
{
int ret;
u16 reg;
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
return ret;
}
reg = ret;
/* Enable automatic full-scale setting mode */
reg &= ~OPT300x_CONFIGURATION_RN_MASK;
reg |= OPT300x_CONFIGURATION_RN_AUTO;
/* Reflect status of the device's integration time setting */
if (reg & OPT300x_CONFIGURATION_CT)
opt->int_time = OPT300x_INT_TIME_LONG;
else
opt->int_time = OPT300x_INT_TIME_SHORT;
/* Ensure device is in shutdown initially */
opt300x_set_mode(opt, &reg, OPT300x_CONFIGURATION_M_SHUTDOWN);
/* Configure for latched window-style comparison operation */
reg |= OPT300x_CONFIGURATION_L;
reg &= ~OPT300x_CONFIGURATION_POL;
reg &= ~OPT300x_CONFIGURATION_ME;
reg &= ~OPT300x_CONFIGURATION_FC_MASK;
ret = i2c_smbus_write_word_swapped(opt->client, OPT300x_CONFIGURATION,
reg);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_CONFIGURATION);
return ret;
}
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_LOW_LIMIT);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_LOW_LIMIT);
return ret;
}
opt->low_thresh_mantissa = OPT300x_REG_MANTISSA(ret);
opt->low_thresh_exp = OPT300x_REG_EXPONENT(ret);
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_HIGH_LIMIT);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_HIGH_LIMIT);
return ret;
}
opt->high_thresh_mantissa = OPT300x_REG_MANTISSA(ret);
opt->high_thresh_exp = OPT300x_REG_EXPONENT(ret);
return 0;
}
static irqreturn_t opt300x_irq(int irq, void *_iio)
{
struct iio_dev *iio = _iio;
struct opt300x *opt = iio_priv(iio);
int ret;
if (!opt->ok_to_ignore_lock)
mutex_lock(&opt->lock);
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
goto out;
}
if ((ret & OPT300x_CONFIGURATION_M_MASK) ==
OPT300x_CONFIGURATION_M_CONTINUOUS) {
if (ret & OPT300x_CONFIGURATION_FH)
iio_push_event(iio,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns());
if (ret & OPT300x_CONFIGURATION_FL)
iio_push_event(iio,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
iio_get_time_ns());
} else if (ret & OPT300x_CONFIGURATION_CRF) {
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_RESULT);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_RESULT);
goto out;
}
opt->result = ret;
opt->result_ready = true;
wake_up(&opt->result_ready_queue);
}
out:
if (!opt->ok_to_ignore_lock)
mutex_unlock(&opt->lock);
return IRQ_HANDLED;
}
static int opt300x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct opt300x_chip *c = (struct opt300x_chip *)id->driver_data;
struct iio_dev *iio;
struct opt300x *opt;
int irq = client->irq;
int ret;
iio = devm_iio_device_alloc(dev, sizeof(*opt));
if (!iio)
return -ENOMEM;
opt = iio_priv(iio);
opt->client = client;
opt->dev = dev;
opt->chip = c;
mutex_init(&opt->lock);
init_waitqueue_head(&opt->result_ready_queue);
i2c_set_clientdata(client, iio);
ret = opt300x_read_id(opt);
if (ret)
return ret;
dev_info(dev, "Found %s\n", id->name);
ret = opt300x_configure(opt);
if (ret)
return ret;
iio->name = client->name;
iio->channels = opt300x_channels;
iio->num_channels = ARRAY_SIZE(opt300x_channels);
iio->dev.parent = dev;
iio->modes = INDIO_DIRECT_MODE;
iio->info = &opt300x_info;
ret = devm_iio_device_register(dev, iio);
if (ret) {
dev_err(dev, "failed to register IIO device\n");
return ret;
}
/* Make use of INT pin only if valid IRQ no. is given */
if (irq > 0) {
ret = request_threaded_irq(irq, NULL, opt300x_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"opt300x", iio);
if (ret) {
dev_err(dev, "failed to request IRQ #%d\n", irq);
return ret;
}
opt->use_irq = true;
} else {
dev_dbg(opt->dev, "enabling interrupt-less operation\n");
}
return 0;
}
static int opt300x_remove(struct i2c_client *client)
{
struct iio_dev *iio = i2c_get_clientdata(client);
struct opt300x *opt = iio_priv(iio);
int ret;
u16 reg;
if (opt->use_irq)
free_irq(client->irq, iio);
ret = i2c_smbus_read_word_swapped(opt->client, OPT300x_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT300x_CONFIGURATION);
return ret;
}
reg = ret;
opt300x_set_mode(opt, &reg, OPT300x_CONFIGURATION_M_SHUTDOWN);
ret = i2c_smbus_write_word_swapped(opt->client, OPT300x_CONFIGURATION,
reg);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT300x_CONFIGURATION);
return ret;
}
return 0;
}
static const struct i2c_device_id opt300x_id[] = {
{ "opt3001", (kernel_ulong_t)&opt3001_chip },
{ "opt3002", (kernel_ulong_t)&opt3002_chip },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(i2c, opt300x_id);
static const struct of_device_id opt300x_of_match[] = {
{ .compatible = "ti,opt3001" },
{ }
};
MODULE_DEVICE_TABLE(of, opt300x_of_match);
static struct i2c_driver opt300x_driver = {
.probe = opt300x_probe,
.remove = opt300x_remove,
.id_table = opt300x_id,
.driver = {
.name = "opt300x",
.of_match_table = of_match_ptr(opt300x_of_match),
},
};
module_i2c_driver(opt300x_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
MODULE_DESCRIPTION("Texas Instruments OPT300x Light Sensor Driver");
......@@ -8,6 +8,6 @@ requires:
kernelModules:
{
$CURDIR/../rtc-pcf85063/rtc-pcf85063
$CURDIR/../mangoh/mangoh_yellow
$CURDIR/../mangoh/mangoh_yellow_dev
}
}
......@@ -61,7 +61,6 @@ apps:
$LEGATO_ROOT/apps/tools/devMode
#if ${MANGOH_BOARD} = YELLOW
$MANGOH_ROOT/samples/BoschSensorsMonitor/boschSensorsMonitor
#endif
}
......@@ -107,6 +106,7 @@ kernelModules:
* Dependencies of the above kernel modules - must be listed explicitly in the SDEF due to the
* way that Legato kernel module dependencies work
*/
$CURDIR/linux_kernel_modules/led/led
$CURDIR/linux_kernel_modules/bq24296/bq24296
$CURDIR/linux_kernel_modules/ltc294x/ltc294x
......@@ -160,13 +160,26 @@ kernelModules:
$CURDIR/linux_kernel_modules/lsm6ds3/lsm6ds3-i2c
$CURDIR/linux_kernel_modules/lsm6ds3/lsm6ds3
#elif ${MANGOH_BOARD} = YELLOW
$CURDIR/linux_kernel_modules/mangoh/mangoh_yellow_dv1
$CURDIR/linux_kernel_modules/mangoh/mangoh_yellow_dev
#if ${MANGOH_KERNEL_LACKS_IIO} = 1
$CURDIR/linux_kernel_modules/iio/iio-triggered-buffer
$CURDIR/linux_kernel_modules/iio/iio-kfifo-buf
$CURDIR/linux_kernel_modules/iio/iio
#endif // MANGOH_KERNEL_LACKS_IIO
// $CURDIR/linux_kernel_modules/bme680/bme680-i2c
// $CURDIR/linux_kernel_modules/bme680/bme680
$CURDIR/linux_kernel_modules/bmi160/bmi160-i2c
$CURDIR/linux_kernel_modules/bmi160/bmi160
$CURDIR/linux_kernel_modules/rtc-pcf85063/rtc-pcf85063
$CURDIR/linux_kernel_modules/rtc_sync/rtc_sync
$CURDIR/linux_kernel_modules/bq25601/bq25601
// $CURDIR/linux_kernel_modules/bq27426/bq27426-i2c
// $CURDIR/linux_kernel_modules/bmm150/bmm150-i2c
$CURDIR/linux_kernel_modules/opt300x/opt300x
$CURDIR/linux_kernel_modules/expander/expander
/*
* Dependencies of the above kernel modules - must be listed explicitly in the SDEF due to the
* way that Legato kernel module dependencies work
*/
$CURDIR/linux_kernel_modules/bme680/bme680-i2c
$CURDIR/linux_kernel_modules/bmi088/bmi088-i2c
#endif // MANGOH_BOARD
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment