diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-27 20:55:39 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-27 20:55:39 +0300 |
commit | 00074a7007c24f947a9f9baf66bf5db2fe651ee9 (patch) | |
tree | 828f1ae0a06b2cf839293ae0c1a49e00c1c59b1f | |
parent | 89d57dddd7d319ded00415790a0bb3c954b7e386 (diff) | |
parent | 9480029fe5c24d482efad38dc631bd555fc7afe2 (diff) | |
download | linux-00074a7007c24f947a9f9baf66bf5db2fe651ee9.tar.xz |
Merge tag 'for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
- test_power: add support for current and charge_counter
- cpcap-charger: improve charge calculation and limit default charge
voltage
- ab8500: convert to IIO
- misc small fixes all over drivers
* tag 'for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (29 commits)
power: supply: bd70528: Add MODULE_ALIAS to allow module auto loading
power: supply: ab8500_charger: Fix inconsistent IS_ERR and PTR_ERR
power: supply: cpcap-charger: cpcap_charger_voltage_to_regval() can be static
power: supply: cpcap-battery: Add basic coulomb counter calibrate support
power: supply: cpcap-battery: Read and save integrator register CCI
power: supply: cpcap-battery: Simplify short term power average calculation
power: supply: cpcap-battery: Simplify coulomb counter calculation with div_s64
power: supply: cpcap-battery: Move coulomb counter units per lsb to ddata
power: supply: cpcap-charger: Allow changing constant charge voltage
power: supply: cpcap-battery: Fix handling of lowered charger voltage
power: supply: cpcap-charger: Improve battery detection
power: supply: cpcap-battery: Check voltage before orderly_poweroff
power: supply: cpcap-charger: Limit voltage to 4.2V for battery
power: supply: ab8500: Handle invalid IRQ from platform_get_irq_byname()
power: supply: ab8500_fg: Do not free non-requested IRQs in probe's error path
power: supply: ab8500: Cleanup probe in reverse order
power: reset: at91: fix __le32 cast in reset code
power: supply: abx500_chargalg: Fix code indentation
mfd: Switch the AB8500 GPADC to IIO
iio: adc: New driver for the AB8500 GPADC
...
26 files changed, 2044 insertions, 2030 deletions
diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index cd9e90c5d171..b6bc30d7777e 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -69,6 +69,18 @@ Required child device properties: - compatible : "stericsson,ab8500-[bm|btemp|charger|fg|gpadc|gpio|ponkey| pwm|regulator|rtc|sysctrl|usb]"; + A few child devices require ADC channels from the GPADC node. Those follow the + standard bindings from iio/iio-bindings.txt and iio/adc/adc.txt + + abx500-temp : io-channels "aux1" and "aux2" for measuring external + temperatures. + ab8500-fg : io-channel "main_bat_v" for measuring main battery voltage, + ab8500-btemp : io-channels "btemp_ball" and "bat_ctrl" for measuring the + battery voltage. + ab8500-charger : io-channels "main_charger_v", "main_charger_c", "vbus_v", + "usb_charger_c" for measuring voltage and current of the + different charging supplies. + Optional child device properties: - interrupts : contains the device IRQ(s) using the 2-cell format (see above) - interrupt-names : contains names of IRQ resource in the order in which they were @@ -102,8 +114,115 @@ ab8500 { 39 0x4>; interrupt-names = "HW_CONV_END", "SW_CONV_END"; vddadc-supply = <&ab8500_ldo_tvout_reg>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + + /* GPADC channels */ + bat_ctrl: channel@1 { + reg = <0x01>; + }; + btemp_ball: channel@2 { + reg = <0x02>; + }; + main_charger_v: channel@3 { + reg = <0x03>; + }; + acc_detect1: channel@4 { + reg = <0x04>; + }; + acc_detect2: channel@5 { + reg = <0x05>; + }; + adc_aux1: channel@6 { + reg = <0x06>; + }; + adc_aux2: channel@7 { + reg = <0x07>; + }; + main_batt_v: channel@8 { + reg = <0x08>; + }; + vbus_v: channel@9 { + reg = <0x09>; + }; + main_charger_c: channel@a { + reg = <0x0a>; + }; + usb_charger_c: channel@b { + reg = <0x0b>; + }; + bk_bat_v: channel@c { + reg = <0x0c>; + }; + die_temp: channel@d { + reg = <0x0d>; + }; + usb_id: channel@e { + reg = <0x0e>; + }; + xtal_temp: channel@12 { + reg = <0x12>; + }; + vbat_true_meas: channel@13 { + reg = <0x13>; + }; + bat_ctrl_and_ibat: channel@1c { + reg = <0x1c>; + }; + vbat_meas_and_ibat: channel@1d { + reg = <0x1d>; + }; + vbat_true_meas_and_ibat: channel@1e { + reg = <0x1e>; + }; + bat_temp_and_ibat: channel@1f { + reg = <0x1f>; + }; }; + ab8500_temp { + compatible = "stericsson,abx500-temp"; + io-channels = <&gpadc 0x06>, + <&gpadc 0x07>; + io-channel-name = "aux1", "aux2"; + }; + + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + thermistor-on-batctrl; + }; + + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + io-channels = <&gpadc 0x08>; + io-channel-name = "main_bat_v"; + }; + + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + io-channels = <&gpadc 0x02>, + <&gpadc 0x01>; + io-channel-name = "btemp_ball", + "bat_ctrl"; + }; + + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + vddadc-supply = <&ab8500_ldo_tvout_reg>; + io-channels = <&gpadc 0x03>, + <&gpadc 0x0a>, + <&gpadc 0x09>, + <&gpadc 0x0b>; + io-channel-name = "main_charger_v", + "main_charger_c", + "vbus_v", + "usb_charger_c"; + }; + ab8500-usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/Documentation/devicetree/bindings/power/supply/cpcap-charger.txt b/Documentation/devicetree/bindings/power/supply/cpcap-charger.txt index 80bd873c3b1d..6048f636783f 100644 --- a/Documentation/devicetree/bindings/power/supply/cpcap-charger.txt +++ b/Documentation/devicetree/bindings/power/supply/cpcap-charger.txt @@ -5,7 +5,8 @@ Required properties: - interrupts: Interrupt specifier for each name in interrupt-names - interrupt-names: Should contain the following entries: "chrg_det", "rvrs_chrg", "chrg_se1b", "se0conn", - "rvrs_mode", "chrgcurr1", "vbusvld", "battdetb" + "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", + "battdetb" - io-channels: IIO ADC channel specifier for each name in io-channel-names - io-channel-names: Should contain the following entries: "battdetb", "battp", "vbus", "chg_isense", "batti" @@ -21,11 +22,13 @@ cpcap_charger: charger { compatible = "motorola,mapphone-cpcap-charger"; interrupts-extended = < &cpcap 13 0 &cpcap 12 0 &cpcap 29 0 &cpcap 28 0 - &cpcap 22 0 &cpcap 20 0 &cpcap 19 0 &cpcap 54 0 + &cpcap 22 0 &cpcap 21 0 &cpcap 20 0 &cpcap 19 0 + &cpcap 54 0 >; interrupt-names = "chrg_det", "rvrs_chrg", "chrg_se1b", "se0conn", - "rvrs_mode", "chrgcurr1", "vbusvld", "battdetb"; + "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", + "battdetb"; mode-gpios = <&gpio3 29 GPIO_ACTIVE_LOW &gpio3 23 GPIO_ACTIVE_LOW>; io-channels = <&cpcap_adc 0 &cpcap_adc 1 diff --git a/MAINTAINERS b/MAINTAINERS index 08bbf9649a1b..ee326c051612 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2021,6 +2021,7 @@ F: drivers/dma/ste_dma40* F: drivers/hwspinlock/u8500_hsem.c F: drivers/i2c/busses/i2c-nomadik.c F: drivers/i2c/busses/i2c-stu300.c +F: drivers/iio/adc/ab8500-gpadc.c F: drivers/mfd/ab3100* F: drivers/mfd/ab8500* F: drivers/mfd/abx500* diff --git a/arch/arm/boot/dts/motorola-cpcap-mapphone.dtsi b/arch/arm/boot/dts/motorola-cpcap-mapphone.dtsi index d1eae47b83f6..08bae935605c 100644 --- a/arch/arm/boot/dts/motorola-cpcap-mapphone.dtsi +++ b/arch/arm/boot/dts/motorola-cpcap-mapphone.dtsi @@ -43,11 +43,13 @@ compatible = "motorola,mapphone-cpcap-charger"; interrupts-extended = < &cpcap 13 0 &cpcap 12 0 &cpcap 29 0 &cpcap 28 0 - &cpcap 22 0 &cpcap 20 0 &cpcap 19 0 &cpcap 54 0 + &cpcap 22 0 &cpcap 21 0 &cpcap 20 0 &cpcap 19 0 + &cpcap 54 0 >; interrupt-names = "chrg_det", "rvrs_chrg", "chrg_se1b", "se0conn", - "rvrs_mode", "chrgcurr1", "vbusvld", "battdetb"; + "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", + "battdetb"; mode-gpios = <&gpio3 29 GPIO_ACTIVE_LOW &gpio3 23 GPIO_ACTIVE_LOW>; io-channels = <&cpcap_adc 0 &cpcap_adc 1 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 13a6b4afb4b3..5308c59d7001 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -40,7 +40,8 @@ comment "Native drivers" config SENSORS_AB8500 tristate "AB8500 thermal monitoring" - depends on AB8500_GPADC && AB8500_BM + depends on AB8500_GPADC && AB8500_BM && (IIO = y) + default n help If you say yes here you get support for the thermal sensor part of the AB8500 chip. The driver includes thermal management for diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c index 207f77f85a40..53f3379d799d 100644 --- a/drivers/hwmon/ab8500.c +++ b/drivers/hwmon/ab8500.c @@ -17,20 +17,24 @@ #include <linux/hwmon-sysfs.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/power/ab8500.h> #include <linux/reboot.h> #include <linux/slab.h> #include <linux/sysfs.h> +#include <linux/iio/consumer.h> #include "abx500.h" #define DEFAULT_POWER_OFF_DELAY (HZ * 10) #define THERMAL_VCC 1800 #define PULL_UP_RESISTOR 47000 -/* Number of monitored sensors should not greater than NUM_SENSORS */ -#define NUM_MONITORED_SENSORS 4 + +#define AB8500_SENSOR_AUX1 0 +#define AB8500_SENSOR_AUX2 1 +#define AB8500_SENSOR_BTEMP_BALL 2 +#define AB8500_SENSOR_BAT_CTRL 3 +#define NUM_MONITORED_SENSORS 4 struct ab8500_gpadc_cfg { const struct abx500_res_to_temp *temp_tbl; @@ -40,7 +44,8 @@ struct ab8500_gpadc_cfg { }; struct ab8500_temp { - struct ab8500_gpadc *gpadc; + struct iio_channel *aux1; + struct iio_channel *aux2; struct ab8500_btemp *btemp; struct delayed_work power_off_work; struct ab8500_gpadc_cfg cfg; @@ -82,15 +87,21 @@ static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) int voltage, ret; struct ab8500_temp *ab8500_data = data->plat_data; - if (sensor == BAT_CTRL) { - *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); - } else if (sensor == BTEMP_BALL) { + if (sensor == AB8500_SENSOR_BTEMP_BALL) { *temp = ab8500_btemp_get_temp(ab8500_data->btemp); - } else { - voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor); - if (voltage < 0) - return voltage; - + } else if (sensor == AB8500_SENSOR_BAT_CTRL) { + *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); + } else if (sensor == AB8500_SENSOR_AUX1) { + ret = iio_read_channel_processed(ab8500_data->aux1, &voltage); + if (ret < 0) + return ret; + ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); + if (ret < 0) + return ret; + } else if (sensor == AB8500_SENSOR_AUX2) { + ret = iio_read_channel_processed(ab8500_data->aux2, &voltage); + if (ret < 0) + return ret; ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); if (ret < 0) return ret; @@ -164,10 +175,6 @@ int abx500_hwmon_init(struct abx500_temp *data) if (!ab8500_data) return -ENOMEM; - ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - if (IS_ERR(ab8500_data->gpadc)) - return PTR_ERR(ab8500_data->gpadc); - ab8500_data->btemp = ab8500_btemp_get(); if (IS_ERR(ab8500_data->btemp)) return PTR_ERR(ab8500_data->btemp); @@ -181,15 +188,25 @@ int abx500_hwmon_init(struct abx500_temp *data) ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; data->plat_data = ab8500_data; + ab8500_data->aux1 = devm_iio_channel_get(&data->pdev->dev, "aux1"); + if (IS_ERR(ab8500_data->aux1)) { + if (PTR_ERR(ab8500_data->aux1) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&data->pdev->dev, "failed to get AUX1 ADC channel\n"); + return PTR_ERR(ab8500_data->aux1); + } + ab8500_data->aux2 = devm_iio_channel_get(&data->pdev->dev, "aux2"); + if (IS_ERR(ab8500_data->aux2)) { + if (PTR_ERR(ab8500_data->aux2) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&data->pdev->dev, "failed to get AUX2 ADC channel\n"); + return PTR_ERR(ab8500_data->aux2); + } - /* - * ADC_AUX1 and ADC_AUX2, connected to external NTC - * BTEMP_BALL and BAT_CTRL, fixed usage - */ - data->gpadc_addr[0] = ADC_AUX1; - data->gpadc_addr[1] = ADC_AUX2; - data->gpadc_addr[2] = BTEMP_BALL; - data->gpadc_addr[3] = BAT_CTRL; + data->gpadc_addr[0] = AB8500_SENSOR_AUX1; + data->gpadc_addr[1] = AB8500_SENSOR_AUX2; + data->gpadc_addr[2] = AB8500_SENSOR_BTEMP_BALL; + data->gpadc_addr[3] = AB8500_SENSOR_BAT_CTRL; data->monitored_sensors = NUM_MONITORED_SENSORS; data->ops.read_sensor = ab8500_read_sensor; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f0af3a42f53c..0b21dd405dd5 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -6,6 +6,16 @@ menu "Analog to digital converters" +config AB8500_GPADC + bool "ST-Ericsson AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + help + AB8500 Analog Baseband, mixed signal integrated circuit GPADC + (General Purpose Analog to Digital Converter) driver used to monitor + internal voltages, convert accessory and battery, AC (charger, mains) + and USB voltages integral to the U8500 platform. + config AD_SIGMA_DELTA tristate select IIO_BUFFER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ef9cc485fb67..fc1b6ebb0cde 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7266) += ad7266.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c new file mode 100644 index 000000000000..fd5b18d7f0c2 --- /dev/null +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + * Author: Johan Palsson <johan.palsson@stericsson.com> + * Author: M'boumba Cedric Madianga + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages: + * VinVADC, and VADC relative to GND to do its job. It monitors main and backup + * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages + * representing the temperature of the chip die and battery, accessory + * detection by resistance measurements using relative voltages and GSM burst + * information. + * + * Some of the voltages are measured on external pins on the IC, such as + * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails + * from other parts of the ASIC such as main charger voltage, main and battery + * backup voltage or USB VBUS voltage. For this reason drivers for other + * parts of the system are required to obtain handles to the ADC to do work + * for them and the IIO driver provides arbitration among these consumers. + */ +#include <linux/init.h> +#include <linux/bits.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/random.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500.h> + +/* GPADC register offsets and bit definitions */ + +#define AB8500_GPADC_CTRL1_REG 0x00 +/* GPADC control register 1 bits */ +#define AB8500_GPADC_CTRL1_DISABLE 0x00 +#define AB8500_GPADC_CTRL1_ENABLE BIT(0) +#define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1) +#define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2) +#define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3) +/* 0 = use rising edge, 1 = use falling edge */ +#define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4) +/* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */ +#define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5) +#define AB8500_GPADC_CTRL1_BUF_ENA BIT(6) +#define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7) + +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +/* + * GPADC control register 2 and 3 bits + * the bit layout is the same for SW and HW conversion set-up + */ +#define AB8500_GPADC_CTRL2_AVG_1 0x00 +#define AB8500_GPADC_CTRL2_AVG_4 BIT(5) +#define AB8500_GPADC_CTRL2_AVG_8 BIT(6) +#define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6)) + +enum ab8500_gpadc_channel { + AB8500_GPADC_CHAN_UNUSED = 0x00, + AB8500_GPADC_CHAN_BAT_CTRL = 0x01, + AB8500_GPADC_CHAN_BAT_TEMP = 0x02, + /* This is not used on AB8505 */ + AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03, + AB8500_GPADC_CHAN_ACC_DET_1 = 0x04, + AB8500_GPADC_CHAN_ACC_DET_2 = 0x05, + AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06, + AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07, + AB8500_GPADC_CHAN_VBAT_A = 0x08, + AB8500_GPADC_CHAN_VBUS = 0x09, + AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a, + AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b, + AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c, + /* Only on AB8505 */ + AB8505_GPADC_CHAN_DIE_TEMP = 0x0d, + AB8500_GPADC_CHAN_ID = 0x0e, + AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f, + AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10, + AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11, + /* FIXME: Applicable to all ASIC variants? */ + AB8500_GPADC_CHAN_XTAL_TEMP = 0x12, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13, + /* FIXME: Doesn't seem to work with pure AB8500 */ + AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c, + AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e, + AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f, + /* + * Virtual channel used only for ibat conversion to ampere. + * Battery current conversion (ibat) cannot be requested as a + * single conversion but it is always requested in combination + * with other input requests. + */ + AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF, +}; + +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 + +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_STAT_BUSY BIT(0) + +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 +#define AB8540_GPADC_MANDATA2L_REG 0x09 +#define AB8540_GPADC_MANDATA2H_REG 0x0A +#define AB8540_GPADC_APEAAX_REG 0x10 +#define AB8540_GPADC_APEAAT_REG 0x11 +#define AB8540_GPADC_APEAAM_REG 0x12 +#define AB8540_GPADC_APEAAH_REG 0x13 +#define AB8540_GPADC_APEAAL_REG 0x14 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 +/* New calibration for 8540 */ +#define AB8540_GPADC_OTP4_REG_7 0x38 +#define AB8540_GPADC_OTP4_REG_6 0x39 +#define AB8540_GPADC_OTP4_REG_5 0x3A + +#define AB8540_GPADC_DIS_ZERO 0x00 +#define AB8540_GPADC_EN_VBIAS_XTAL_TEMP 0x02 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define AB8500_ADC_RESOLUTION 1024 +#define AB8500_ADC_CH_BTEMP_MIN 0 +#define AB8500_ADC_CH_BTEMP_MAX 1350 +#define AB8500_ADC_CH_DIETEMP_MIN 0 +#define AB8500_ADC_CH_DIETEMP_MAX 1350 +#define AB8500_ADC_CH_CHG_V_MIN 0 +#define AB8500_ADC_CH_CHG_V_MAX 20030 +#define AB8500_ADC_CH_ACCDET2_MIN 0 +#define AB8500_ADC_CH_ACCDET2_MAX 2500 +#define AB8500_ADC_CH_VBAT_MIN 2300 +#define AB8500_ADC_CH_VBAT_MAX 4800 +#define AB8500_ADC_CH_CHG_I_MIN 0 +#define AB8500_ADC_CH_CHG_I_MAX 1500 +#define AB8500_ADC_CH_BKBAT_MIN 0 +#define AB8500_ADC_CH_BKBAT_MAX 3200 + +/* GPADC constants from AB8540 spec */ +#define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX 6000 +#define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX_V 60 +#define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ +#define AB8500_GPADC_IBAT_VDROP_H 56 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define AB8500_GPADC_CALIB_SCALE 1000 +/* + * Number of bits shift used to not lose precision + * when dividing to get ibat gain. + */ +#define AB8500_GPADC_CALIB_SHIFT_IBAT 20 + +/* Time in ms before disabling regulator */ +#define AB8500_GPADC_AUTOSUSPEND_DELAY 1 + +#define AB8500_GPADC_CONVERSION_TIME 500 /* ms */ + +enum ab8500_cal_channels { + AB8500_CAL_VMAIN = 0, + AB8500_CAL_BTEMP, + AB8500_CAL_VBAT, + AB8500_CAL_IBAT, + AB8500_CAL_NR, +}; + +/** + * struct ab8500_adc_cal_data - Table for storing gain and offset for the + * calibrated ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + * @otp_calib_hi: Calibration from OTP + * @otp_calib_lo: Calibration from OTP + */ +struct ab8500_adc_cal_data { + s64 gain; + s64 offset; + u16 otp_calib_hi; + u16 otp_calib_lo; +}; + +/** + * struct ab8500_gpadc_chan_info - per-channel GPADC info + * @name: name of the channel + * @id: the internal AB8500 ID number for the channel + * @hardware_control: indicate that we want to use hardware ADC control + * on this channel, the default is software ADC control. Hardware control + * is normally only used to test the battery voltage during GSM bursts + * and needs a hardware trigger on the GPADCTrig pin of the ASIC. + * @falling_edge: indicate that we want to trigger on falling edge + * rather than rising edge, rising edge is the default + * @avg_sample: how many samples to average: must be 1, 4, 8 or 16. + * @trig_timer: how long to wait for the trigger, in 32kHz periods: + * 0 .. 255 periods + */ +struct ab8500_gpadc_chan_info { + const char *name; + u8 id; + bool hardware_control; + bool falling_edge; + u8 avg_sample; + u8 trig_timer; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the containing device + * @ab8500: pointer to the parent AB8500 device + * @chans: internal per-channel information container + * @nchans: number of channels + * @complete: pointer to the completion that indicates + * the completion of an gpadc conversion cycle + * @vddadc: pointer to the regulator supplying VDDADC + * @irq_sw: interrupt number that is used by gpadc for software ADC conversion + * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion + * @cal_data: array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct ab8500 *ab8500; + struct ab8500_gpadc_chan_info *chans; + unsigned int nchans; + struct completion complete; + struct regulator *vddadc; + int irq_sw; + int irq_hw; + struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR]; +}; + +static struct ab8500_gpadc_chan_info * +ab8500_gpadc_get_channel(struct ab8500_gpadc *gpadc, u8 chan) +{ + struct ab8500_gpadc_chan_info *ch; + int i; + + for (i = 0; i < gpadc->nchans; i++) { + ch = &gpadc->chans[i]; + if (ch->id == chan) + break; + } + if (i == gpadc->nchans) + return NULL; + + return ch; +} + +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + * @gpadc: GPADC instance + * @ch: the sampled channel this raw value is coming from + * @ad_value: the raw value + */ +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + enum ab8500_gpadc_channel ch, + int ad_value) +{ + int res; + + switch (ch) { + case AB8500_GPADC_CHAN_MAIN_CHARGER: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) { + res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX - + AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain + + gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_BAT_CTRL: + case AB8500_GPADC_CHAN_BAT_TEMP: + case AB8500_GPADC_CHAN_ACC_DET_1: + case AB8500_GPADC_CHAN_ADC_AUX_1: + case AB8500_GPADC_CHAN_ADC_AUX_2: + case AB8500_GPADC_CHAN_XTAL_TEMP: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) { + res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX - + AB8500_ADC_CH_BTEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain + + gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_VBAT_A: + case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) { + res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX - + AB8500_ADC_CH_VBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain + + gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8505_GPADC_CHAN_DIE_TEMP: + res = AB8500_ADC_CH_DIETEMP_MIN + + (AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_ACC_DET_2: + res = AB8500_ADC_CH_ACCDET2_MIN + + (AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_VBUS: + res = AB8500_ADC_CH_CHG_V_MIN + + (AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + res = AB8500_ADC_CH_CHG_I_MIN + + (AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_BACKUP_BAT: + res = AB8500_ADC_CH_BKBAT_MIN + + (AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_IBAT_VIRTUAL: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) { + res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX - + AB8500_ADC_CH_IBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain + + gpadc->cal_data[AB8500_CAL_IBAT].offset) + >> AB8500_GPADC_CALIB_SHIFT_IBAT; + break; + + default: + dev_err(gpadc->dev, + "unknown channel ID: %d, not possible to convert\n", + ch); + res = -EINVAL; + break; + + } + + return res; +} + +static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc, + const struct ab8500_gpadc_chan_info *ch, + int *ibat) +{ + int ret; + int looplimit = 0; + unsigned long completion_timeout; + u8 val; + u8 low_data, high_data, low_data2, high_data2; + u8 ctrl1; + u8 ctrl23; + unsigned int delay_min = 0; + unsigned int delay_max = 0; + u8 data_low_addr, data_high_addr; + + if (!gpadc) + return -ENODEV; + + /* check if conversion is supported */ + if ((gpadc->irq_sw <= 0) && !ch->hardware_control) + return -ENOTSUPP; + if ((gpadc->irq_hw <= 0) && ch->hardware_control) + return -ENOTSUPP; + + /* Enable vddadc by grabbing PM runtime */ + pm_runtime_get_sync(gpadc->dev); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & AB8500_GPADC_STAT_BUSY)) + break; + msleep(20); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ctrl1 = AB8500_GPADC_CTRL1_ENABLE; + + /* Select the channel source and set average samples */ + switch (ch->avg_sample) { + case 1: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1; + break; + case 4: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4; + break; + case 8: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8; + break; + default: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16; + break; + } + + if (ch->hardware_control) { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23); + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA; + if (ch->falling_edge) + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE; + } else { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23); + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed, ABB 3.0 needs some special + * treatment too. + */ + switch (ch->id) { + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_ICHAR_ENA; + break; + case AB8500_GPADC_CHAN_BAT_TEMP: + if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) { + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_BTEMP_PULL_UP; + /* + * Delay might be needed for ABB8500 cut 3.0, if not, + * remove when hardware will be available + */ + delay_min = 1000; /* Delay in micro seconds */ + delay_max = 10000; /* large range optimises sleepmode */ + break; + } + /* Fall through */ + default: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA; + break; + } + + /* Write configuration to control register 1 */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set Control register failed\n"); + goto out; + } + + if (delay_min != 0) + usleep_range(delay_min, delay_max); + + if (ch->hardware_control) { + /* Set trigger delay timer */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, + ch->trig_timer); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: trig timer failed\n"); + goto out; + } + completion_timeout = 2 * HZ; + data_low_addr = AB8500_GPADC_AUTODATAL_REG; + data_high_addr = AB8500_GPADC_AUTODATAH_REG; + } else { + /* Start SW conversion */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + AB8500_GPADC_CTRL1_START_SW_CONV, + AB8500_GPADC_CTRL1_START_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conv failed\n"); + goto out; + } + completion_timeout = msecs_to_jiffies(AB8500_GPADC_CONVERSION_TIME); + data_low_addr = AB8500_GPADC_MANDATAL_REG; + data_high_addr = AB8500_GPADC_MANDATAH_REG; + } + + /* Wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->complete, + completion_timeout)) { + dev_err(gpadc->dev, + "timeout didn't receive GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_low_addr, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_high_addr, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + /* Check if double conversion is required */ + if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) { + + if (ch->hardware_control) { + /* not supported */ + ret = -ENOTSUPP; + dev_err(gpadc->dev, + "gpadc_conversion: only SW double conversion supported\n"); + goto out; + } else { + /* Read the converted RAW data 2 */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, + &low_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data 2 failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, + &high_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data 2 failed\n"); + goto out; + } + if (ibat != NULL) { + *ibat = (high_data2 << 8) | low_data2; + } else { + dev_warn(gpadc->dev, + "gpadc_conversion: ibat not stored\n"); + } + + } + } + + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + + /* This eventually drops the regulator */ + pm_runtime_mark_last_busy(gpadc->dev); + pm_runtime_put_autosuspend(gpadc->dev); + + return (high_data << 8) | low_data; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + pm_runtime_put(gpadc->dev); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", ch->id); + + return ret; +} + +/** + * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data) +{ + struct ab8500_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static int otp4_cal_regs[] = { + AB8540_GPADC_OTP4_REG_7, + AB8540_GPADC_OTP4_REG_6, + AB8540_GPADC_OTP4_REG_5, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; + u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + int ibat_high, ibat_low; + s64 V_gain, V_offset, V2A_gain, V2A_offset; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) { + /* Continue anyway: maybe the other registers are OK */ + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } else { + /* Put this in the entropy pool as device-unique */ + add_device_randomness(&ret[i], sizeof(ret[i])); + } + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits 8500/9540: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP2 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP4 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | ib_h9 | ib_h8 | ib_h7 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + if (is_ab8540(gpadc->ab8500)) { + /* Calculate gain and offset for VMAIN if all reads succeeded*/ + if (!(ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + + /* Read IBAT calibration Data */ + for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { + ret_otp4[i] = abx500_get_register_interruptible( + gpadc->dev, AB8500_OTP_EMUL, + otp4_cal_regs[i], &gpadc_otp4[i]); + if (ret_otp4[i] < 0) + dev_err(gpadc->dev, + "%s: read otp4 reg 0x%02x failed\n", + __func__, otp4_cal_regs[i]); + } + + /* Calculate gain and offset for IBAT if all reads succeeded */ + if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { + ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | + ((gpadc_otp4[1] & 0xFE) >> 1)); + ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | + ((gpadc_otp4[2] & 0xF8) >> 3)); + + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_hi = + (u16)ibat_high; + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo = + (u16)ibat_low; + + V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) + << AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); + + V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) - + (((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) << + AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) + * ibat_high; + /* + * Result obtained is in mV (at a scale factor), + * we need to calculate gain and offset to get mA + */ + V2A_gain = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/ + (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN - + AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V) + << AB8500_GPADC_CALIB_SHIFT_IBAT) + / (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + + gpadc->cal_data[AB8500_CAL_IBAT].gain = + V_gain * V2A_gain; + gpadc->cal_data[AB8500_CAL_IBAT].offset = + V_offset * V2A_gain + V2A_offset; + } else { + gpadc->cal_data[AB8500_CAL_IBAT].gain = 0; + } + } else { + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high; + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low; + + gpadc->cal_data[AB8500_CAL_BTEMP].gain = + AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 - + (AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) + * btemp_high; + } else { + gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high; + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low; + + gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 - + (AB8500_GPADC_CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[AB8500_CAL_VBAT].gain = 0; + } +} + +static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + const struct ab8500_gpadc_chan_info *ch; + int raw_val; + int processed; + + ch = ab8500_gpadc_get_channel(gpadc, chan->address); + if (!ch) { + dev_err(gpadc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + + raw_val = ab8500_gpadc_read(gpadc, ch, NULL); + if (raw_val < 0) + return raw_val; + + if (mask == IIO_CHAN_INFO_RAW) { + *val = raw_val; + return IIO_VAL_INT; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) { + processed = ab8500_gpadc_ad_to_voltage(gpadc, ch->id, raw_val); + if (processed < 0) + return processed; + + /* Return millivolt or milliamps or millicentigrades */ + *val = processed * 1000; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info ab8500_gpadc_info = { + .of_xlate = ab8500_gpadc_of_xlate, + .read_raw = ab8500_gpadc_read_raw, +}; + +#ifdef CONFIG_PM +static int ab8500_gpadc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + regulator_disable(gpadc->vddadc); + + return 0; +} + +static int ab8500_gpadc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gpadc->vddadc); + if (ret) + dev_err(dev, "Failed to enable vddadc: %d\n", ret); + + return ret; +} +#endif + +/** + * ab8500_gpadc_parse_channel() - process devicetree channel configuration + * @dev: pointer to containing device + * @np: device tree node for the channel to configure + * @ch: channel info to fill in + * @iio_chan: IIO channel specification to fill in + * + * The devicetree will set up the channel for use with the specific device, + * and define usage for things like AUX GPADC inputs more precisely. + */ +static int ab8500_gpadc_parse_channel(struct device *dev, + struct device_node *np, + struct ab8500_gpadc_chan_info *ch, + struct iio_chan_spec *iio_chan) +{ + const char *name = np->name; + u32 chan; + int ret; + + ret = of_property_read_u32(np, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) { + dev_err(dev, "%s channel number out of range %d\n", name, chan); + return -EINVAL; + } + + iio_chan->channel = chan; + iio_chan->datasheet_name = name; + iio_chan->indexed = 1; + iio_chan->address = chan; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + /* Most are voltages (also temperatures), some are currents */ + if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) || + (chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT)) + iio_chan->type = IIO_CURRENT; + else + iio_chan->type = IIO_VOLTAGE; + + ch->id = chan; + + /* Sensible defaults */ + ch->avg_sample = 16; + ch->hardware_control = false; + ch->falling_edge = false; + ch->trig_timer = 0; + + return 0; +} + +/** + * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT + * @gpadc: the GPADC to configure the channels for + * @np: device tree node containing the channel configurations + * @chans: the IIO channels we parsed + * @nchans: the number of IIO channels we parsed + */ +static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, + struct device_node *np, + struct iio_chan_spec **chans_parsed, + unsigned int *nchans_parsed) +{ + struct device_node *child; + struct ab8500_gpadc_chan_info *ch; + struct iio_chan_spec *iio_chans; + unsigned int nchans; + int i; + + nchans = of_get_available_child_count(np); + if (!nchans) { + dev_err(gpadc->dev, "no channel children\n"); + return -ENODEV; + } + dev_info(gpadc->dev, "found %d ADC channels\n", nchans); + + iio_chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*iio_chans), GFP_KERNEL); + if (!iio_chans) + return -ENOMEM; + + gpadc->chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*gpadc->chans), GFP_KERNEL); + if (!gpadc->chans) + return -ENOMEM; + + i = 0; + for_each_available_child_of_node(np, child) { + struct iio_chan_spec *iio_chan; + int ret; + + ch = &gpadc->chans[i]; + iio_chan = &iio_chans[i]; + + ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, + iio_chan); + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + gpadc->nchans = nchans; + *chans_parsed = iio_chans; + *nchans_parsed = nchans; + + return 0; +} + +static int ab8500_gpadc_probe(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct iio_chan_spec *iio_chans; + unsigned int n_iio_chans; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + gpadc = iio_priv(indio_dev); + + gpadc->dev = dev; + gpadc->ab8500 = dev_get_drvdata(dev->parent); + + ret = ab8500_gpadc_parse_channels(gpadc, np, &iio_chans, &n_iio_chans); + if (ret) + return ret; + + gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq_sw < 0) { + dev_err(dev, "failed to get platform sw_conv_end irq\n"); + return gpadc->irq_sw; + } + + gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); + if (gpadc->irq_hw < 0) { + dev_err(dev, "failed to get platform hw_conv_end irq\n"); + return gpadc->irq_hw; + } + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->complete); + + /* Request interrupts */ + ret = devm_request_threaded_irq(dev, gpadc->irq_sw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-sw", gpadc); + if (ret < 0) { + dev_err(dev, + "failed to request sw conversion irq %d\n", + gpadc->irq_sw); + return ret; + } + + ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-hw", gpadc); + if (ret < 0) { + dev_err(dev, + "Failed to request hw conversion irq: %d\n", + gpadc->irq_hw); + return ret; + } + + /* The VTVout LDO used to power the AB8500 GPADC */ + gpadc->vddadc = devm_regulator_get(dev, "vddadc"); + if (IS_ERR(gpadc->vddadc)) { + ret = PTR_ERR(gpadc->vddadc); + dev_err(dev, "failed to get vddadc\n"); + return ret; + } + + ret = regulator_enable(gpadc->vddadc); + if (ret) { + dev_err(dev, "failed to enable vddadc: %d\n", ret); + return ret; + } + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, AB8500_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + + ab8500_gpadc_read_calibration_data(gpadc); + + pm_runtime_put(dev); + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = np; + indio_dev->name = "ab8500-gpadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ab8500_gpadc_info; + indio_dev->channels = iio_chans; + indio_dev->num_channels = n_iio_chans; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + goto out_dis_pm; + + return 0; + +out_dis_pm: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + regulator_disable(gpadc->vddadc); + + return ret; +} + +static int ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + pm_runtime_get_sync(gpadc->dev); + pm_runtime_put_noidle(gpadc->dev); + pm_runtime_disable(gpadc->dev); + regulator_disable(gpadc->vddadc); + + return 0; +} + +static const struct dev_pm_ops ab8500_gpadc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, + ab8500_gpadc_runtime_resume, + NULL) +}; + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = ab8500_gpadc_remove, + .driver = { + .name = "ab8500-gpadc", + .pm = &ab8500_gpadc_pm_ops, + }, +}; +builtin_platform_driver(ab8500_gpadc_driver); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ae24d3ea68ea..420900852166 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1210,13 +1210,6 @@ config AB8500_DEBUG Select this option if you want debug information using the debug filesystem, debugfs. -config AB8500_GPADC - bool "ST-Ericsson AB8500 GPADC driver" - depends on AB8500_CORE && REGULATOR_AB8500 - default y - help - AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c1067ea46204..aed99f08739f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -177,7 +177,6 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o -obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o # ab8500-core need to come after db8500-prcmu (which provides the channel) obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index f4e26b6e5362..1a9a3414d4fa 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -84,7 +84,6 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #ifdef CONFIG_DEBUG_FS #include <linux/string.h> @@ -103,11 +102,6 @@ static int num_irqs; static struct device_attribute **dev_attr; static char **event_name; -static u8 avg_sample = SAMPLE_16; -static u8 trig_edge = RISING_EDGE; -static u8 conv_type = ADC_SW; -static u8 trig_timer; - /** * struct ab8500_reg_range * @first: the first address of the range @@ -152,7 +146,6 @@ static struct hwreg_cfg hwreg_cfg = { }; #define AB8500_NAME_STRING "ab8500" -#define AB8500_ADC_NAME_STRING "gpadc" #define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST #define AB8500_REV_REG 0x80 @@ -1646,633 +1639,6 @@ report_write_failure: DEFINE_SHOW_ATTRIBUTE(ab8500_modem); -static int ab8500_gpadc_bat_ctrl_show(struct seq_file *s, void *p) -{ - int bat_ctrl_raw; - int bat_ctrl_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL, - avg_sample, trig_edge, trig_timer, conv_type); - bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BAT_CTRL, bat_ctrl_raw); - - seq_printf(s, "%d,0x%X\n", bat_ctrl_convert, bat_ctrl_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bat_ctrl); - -static int ab8500_gpadc_btemp_ball_show(struct seq_file *s, void *p) -{ - int btemp_ball_raw; - int btemp_ball_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL, - avg_sample, trig_edge, trig_timer, conv_type); - btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, - btemp_ball_raw); - - seq_printf(s, "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_btemp_ball); - -static int ab8500_gpadc_main_charger_v_show(struct seq_file *s, void *p) -{ - int main_charger_v_raw; - int main_charger_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V, - avg_sample, trig_edge, trig_timer, conv_type); - main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_V, main_charger_v_raw); - - seq_printf(s, "%d,0x%X\n", main_charger_v_convert, main_charger_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_v); - -static int ab8500_gpadc_acc_detect1_show(struct seq_file *s, void *p) -{ - int acc_detect1_raw; - int acc_detect1_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1, - avg_sample, trig_edge, trig_timer, conv_type); - acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, - acc_detect1_raw); - - seq_printf(s, "%d,0x%X\n", acc_detect1_convert, acc_detect1_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect1); - -static int ab8500_gpadc_acc_detect2_show(struct seq_file *s, void *p) -{ - int acc_detect2_raw; - int acc_detect2_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2, - avg_sample, trig_edge, trig_timer, conv_type); - acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, - ACC_DETECT2, acc_detect2_raw); - - seq_printf(s, "%d,0x%X\n", acc_detect2_convert, acc_detect2_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect2); - -static int ab8500_gpadc_aux1_show(struct seq_file *s, void *p) -{ - int aux1_raw; - int aux1_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1, - avg_sample, trig_edge, trig_timer, conv_type); - aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, - aux1_raw); - - seq_printf(s, "%d,0x%X\n", aux1_convert, aux1_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux1); - -static int ab8500_gpadc_aux2_show(struct seq_file *s, void *p) -{ - int aux2_raw; - int aux2_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2, - avg_sample, trig_edge, trig_timer, conv_type); - aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, - aux2_raw); - - seq_printf(s, "%d,0x%X\n", aux2_convert, aux2_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux2); - -static int ab8500_gpadc_main_bat_v_show(struct seq_file *s, void *p) -{ - int main_bat_v_raw; - int main_bat_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V, - avg_sample, trig_edge, trig_timer, conv_type); - main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, - main_bat_v_raw); - - seq_printf(s, "%d,0x%X\n", main_bat_v_convert, main_bat_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_bat_v); - -static int ab8500_gpadc_vbus_v_show(struct seq_file *s, void *p) -{ - int vbus_v_raw; - int vbus_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V, - avg_sample, trig_edge, trig_timer, conv_type); - vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, - vbus_v_raw); - - seq_printf(s, "%d,0x%X\n", vbus_v_convert, vbus_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_vbus_v); - -static int ab8500_gpadc_main_charger_c_show(struct seq_file *s, void *p) -{ - int main_charger_c_raw; - int main_charger_c_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C, - avg_sample, trig_edge, trig_timer, conv_type); - main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_C, main_charger_c_raw); - - seq_printf(s, "%d,0x%X\n", main_charger_c_convert, main_charger_c_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_c); - -static int ab8500_gpadc_usb_charger_c_show(struct seq_file *s, void *p) -{ - int usb_charger_c_raw; - int usb_charger_c_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C, - avg_sample, trig_edge, trig_timer, conv_type); - usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - USB_CHARGER_C, usb_charger_c_raw); - - seq_printf(s, "%d,0x%X\n", usb_charger_c_convert, usb_charger_c_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_charger_c); - -static int ab8500_gpadc_bk_bat_v_show(struct seq_file *s, void *p) -{ - int bk_bat_v_raw; - int bk_bat_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V, - avg_sample, trig_edge, trig_timer, conv_type); - bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BK_BAT_V, bk_bat_v_raw); - - seq_printf(s, "%d,0x%X\n", bk_bat_v_convert, bk_bat_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bk_bat_v); - -static int ab8500_gpadc_die_temp_show(struct seq_file *s, void *p) -{ - int die_temp_raw; - int die_temp_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP, - avg_sample, trig_edge, trig_timer, conv_type); - die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, - die_temp_raw); - - seq_printf(s, "%d,0x%X\n", die_temp_convert, die_temp_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_die_temp); - -static int ab8500_gpadc_usb_id_show(struct seq_file *s, void *p) -{ - int usb_id_raw; - int usb_id_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - usb_id_raw = ab8500_gpadc_read_raw(gpadc, USB_ID, - avg_sample, trig_edge, trig_timer, conv_type); - usb_id_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_ID, - usb_id_raw); - - seq_printf(s, "%d,0x%X\n", usb_id_convert, usb_id_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_id); - -static int ab8540_gpadc_xtal_temp_show(struct seq_file *s, void *p) -{ - int xtal_temp_raw; - int xtal_temp_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - xtal_temp_raw = ab8500_gpadc_read_raw(gpadc, XTAL_TEMP, - avg_sample, trig_edge, trig_timer, conv_type); - xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP, - xtal_temp_raw); - - seq_printf(s, "%d,0x%X\n", xtal_temp_convert, xtal_temp_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_xtal_temp); - -static int ab8540_gpadc_vbat_true_meas_show(struct seq_file *s, void *p) -{ - int vbat_true_meas_raw; - int vbat_true_meas_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS, - avg_sample, trig_edge, trig_timer, conv_type); - vbat_true_meas_convert = - ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, - vbat_true_meas_raw); - - seq_printf(s, "%d,0x%X\n", vbat_true_meas_convert, vbat_true_meas_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas); - -static int ab8540_gpadc_bat_ctrl_and_ibat_show(struct seq_file *s, void *p) -{ - int bat_ctrl_raw; - int bat_ctrl_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_ctrl_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_CTRL_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - - bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL, - bat_ctrl_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - bat_ctrl_convert, bat_ctrl_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_ctrl_and_ibat); - -static int ab8540_gpadc_vbat_meas_and_ibat_show(struct seq_file *s, void *p) -{ - int vbat_meas_raw; - int vbat_meas_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_meas_raw = ab8500_gpadc_double_read_raw(gpadc, VBAT_MEAS_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - vbat_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, - vbat_meas_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - vbat_meas_convert, vbat_meas_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_meas_and_ibat); - -static int ab8540_gpadc_vbat_true_meas_and_ibat_show(struct seq_file *s, void *p) -{ - int vbat_true_meas_raw; - int vbat_true_meas_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_true_meas_raw = ab8500_gpadc_double_read_raw(gpadc, - VBAT_TRUE_MEAS_AND_IBAT, avg_sample, trig_edge, - trig_timer, conv_type, &ibat_raw); - vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, - VBAT_TRUE_MEAS, vbat_true_meas_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - vbat_true_meas_convert, vbat_true_meas_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas_and_ibat); - -static int ab8540_gpadc_bat_temp_and_ibat_show(struct seq_file *s, void *p) -{ - int bat_temp_raw; - int bat_temp_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_temp_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_TEMP_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - bat_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, - bat_temp_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - bat_temp_convert, bat_temp_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_temp_and_ibat); - -static int ab8540_gpadc_otp_calib_show(struct seq_file *s, void *p) -{ - struct ab8500_gpadc *gpadc; - u16 vmain_l, vmain_h, btemp_l, btemp_h; - u16 vbat_l, vbat_h, ibat_l, ibat_h; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h, - &vbat_l, &vbat_h, &ibat_l, &ibat_h); - seq_printf(s, - "VMAIN_L:0x%X\n" - "VMAIN_H:0x%X\n" - "BTEMP_L:0x%X\n" - "BTEMP_H:0x%X\n" - "VBAT_L:0x%X\n" - "VBAT_H:0x%X\n" - "IBAT_L:0x%X\n" - "IBAT_H:0x%X\n", - vmain_l, vmain_h, btemp_l, btemp_h, - vbat_l, vbat_h, ibat_l, ibat_h); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_otp_calib); - -static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", avg_sample); - - return 0; -} - -static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_avg_sample_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_avg_sample_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_avg_sample; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_avg_sample); - if (err) - return err; - - if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4) - || (user_avg_sample == SAMPLE_8) - || (user_avg_sample == SAMPLE_16)) { - avg_sample = (u8) user_avg_sample; - } else { - dev_err(dev, - "debugfs err input: should be egal to 1, 4, 8 or 16\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_avg_sample_fops = { - .open = ab8500_gpadc_avg_sample_open, - .read = seq_read, - .write = ab8500_gpadc_avg_sample_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", trig_edge); - - return 0; -} - -static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_trig_edge_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_trig_edge_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_trig_edge; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_trig_edge); - if (err) - return err; - - if ((user_trig_edge == RISING_EDGE) - || (user_trig_edge == FALLING_EDGE)) { - trig_edge = (u8) user_trig_edge; - } else { - dev_err(dev, "Wrong input:\n" - "Enter 0. Rising edge\n" - "Enter 1. Falling edge\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_trig_edge_fops = { - .open = ab8500_gpadc_trig_edge_open, - .read = seq_read, - .write = ab8500_gpadc_trig_edge_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", trig_timer); - - return 0; -} - -static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_trig_timer_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_trig_timer_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_trig_timer; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_trig_timer); - if (err) - return err; - - if (user_trig_timer & ~0xFF) { - dev_err(dev, - "debugfs error input: should be between 0 to 255\n"); - return -EINVAL; - } - - trig_timer = (u8) user_trig_timer; - - return count; -} - -static const struct file_operations ab8500_gpadc_trig_timer_fops = { - .open = ab8500_gpadc_trig_timer_open, - .read = seq_read, - .write = ab8500_gpadc_trig_timer_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", conv_type); - - return 0; -} - -static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_conv_type_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_conv_type_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_conv_type; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_conv_type); - if (err) - return err; - - if ((user_conv_type == ADC_SW) - || (user_conv_type == ADC_HW)) { - conv_type = (u8) user_conv_type; - } else { - dev_err(dev, "Wrong input:\n" - "Enter 0. ADC SW conversion\n" - "Enter 1. ADC HW conversion\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_conv_type_fops = { - .open = ab8500_gpadc_conv_type_open, - .read = seq_read, - .write = ab8500_gpadc_conv_type_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - /* * return length of an ASCII numerical value, 0 is string is not a * numerical value. @@ -2647,7 +2013,6 @@ static const struct file_operations ab8500_hwreg_fops = { static int ab8500_debug_probe(struct platform_device *plf) { struct dentry *ab8500_dir; - struct dentry *ab8500_gpadc_dir; struct ab8500 *ab8500; struct resource *res; @@ -2689,9 +2054,6 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); - ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING, - ab8500_dir); - debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, &plf->dev, &ab8500_bank_registers_fops); debugfs_create_file("all-banks", S_IRUGO, ab8500_dir, @@ -2727,83 +2089,6 @@ static int ab8500_debug_probe(struct platform_device *plf) &plf->dev, &ab8500_hwreg_fops); debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, &plf->dev, &ab8500_modem_fops); - debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_bat_ctrl_fops); - debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_btemp_ball_fops); - debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_charger_v_fops); - debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_acc_detect1_fops); - debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_acc_detect2_fops); - debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_aux1_fops); - debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_aux2_fops); - debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_bat_v_fops); - debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_vbus_v_fops); - debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_charger_c_fops); - debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_usb_charger_c_fops); - debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_bk_bat_v_fops); - debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_die_temp_fops); - debugfs_create_file("usb_id", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_usb_id_fops); - if (is_ab8540(ab8500)) { - debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_xtal_temp_fops); - debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_true_meas_fops); - debugfs_create_file("batctrl_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_bat_ctrl_and_ibat_fops); - debugfs_create_file("vbatmeas_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_meas_and_ibat_fops); - debugfs_create_file("vbattruemeas_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_true_meas_and_ibat_fops); - debugfs_create_file("battemp_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_bat_temp_and_ibat_fops); - debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_otp_calib_fops); - } - debugfs_create_file("avg_sample", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_avg_sample_fops); - debugfs_create_file("trig_edge", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_trig_edge_fops); - debugfs_create_file("trig_timer", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_trig_timer_fops); - debugfs_create_file("conv_type", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_conv_type_fops); return 0; } diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c deleted file mode 100644 index 005f9ee34cd1..000000000000 --- a/drivers/mfd/ab8500-gpadc.c +++ /dev/null @@ -1,1075 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Arun R Murthy <arun.murthy@stericsson.com> - * Author: Daniel Willerud <daniel.willerud@stericsson.com> - * Author: Johan Palsson <johan.palsson@stericsson.com> - * Author: M'boumba Cedric Madianga - */ -#include <linux/init.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> -#include <linux/completion.h> -#include <linux/regulator/consumer.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/mfd/abx500.h> -#include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> - -/* - * GPADC register offsets - * Bank : 0x0A - */ -#define AB8500_GPADC_CTRL1_REG 0x00 -#define AB8500_GPADC_CTRL2_REG 0x01 -#define AB8500_GPADC_CTRL3_REG 0x02 -#define AB8500_GPADC_AUTO_TIMER_REG 0x03 -#define AB8500_GPADC_STAT_REG 0x04 -#define AB8500_GPADC_MANDATAL_REG 0x05 -#define AB8500_GPADC_MANDATAH_REG 0x06 -#define AB8500_GPADC_AUTODATAL_REG 0x07 -#define AB8500_GPADC_AUTODATAH_REG 0x08 -#define AB8500_GPADC_MUX_CTRL_REG 0x09 -#define AB8540_GPADC_MANDATA2L_REG 0x09 -#define AB8540_GPADC_MANDATA2H_REG 0x0A -#define AB8540_GPADC_APEAAX_REG 0x10 -#define AB8540_GPADC_APEAAT_REG 0x11 -#define AB8540_GPADC_APEAAM_REG 0x12 -#define AB8540_GPADC_APEAAH_REG 0x13 -#define AB8540_GPADC_APEAAL_REG 0x14 - -/* - * OTP register offsets - * Bank : 0x15 - */ -#define AB8500_GPADC_CAL_1 0x0F -#define AB8500_GPADC_CAL_2 0x10 -#define AB8500_GPADC_CAL_3 0x11 -#define AB8500_GPADC_CAL_4 0x12 -#define AB8500_GPADC_CAL_5 0x13 -#define AB8500_GPADC_CAL_6 0x14 -#define AB8500_GPADC_CAL_7 0x15 -/* New calibration for 8540 */ -#define AB8540_GPADC_OTP4_REG_7 0x38 -#define AB8540_GPADC_OTP4_REG_6 0x39 -#define AB8540_GPADC_OTP4_REG_5 0x3A - -/* gpadc constants */ -#define EN_VINTCORE12 0x04 -#define EN_VTVOUT 0x02 -#define EN_GPADC 0x01 -#define DIS_GPADC 0x00 -#define AVG_1 0x00 -#define AVG_4 0x20 -#define AVG_8 0x40 -#define AVG_16 0x60 -#define ADC_SW_CONV 0x04 -#define EN_ICHAR 0x80 -#define BTEMP_PULL_UP 0x08 -#define EN_BUF 0x40 -#define DIS_ZERO 0x00 -#define GPADC_BUSY 0x01 -#define EN_FALLING 0x10 -#define EN_TRIG_EDGE 0x02 -#define EN_VBIAS_XTAL_TEMP 0x02 - -/* GPADC constants from AB8500 spec, UM0836 */ -#define ADC_RESOLUTION 1024 -#define ADC_CH_BTEMP_MIN 0 -#define ADC_CH_BTEMP_MAX 1350 -#define ADC_CH_DIETEMP_MIN 0 -#define ADC_CH_DIETEMP_MAX 1350 -#define ADC_CH_CHG_V_MIN 0 -#define ADC_CH_CHG_V_MAX 20030 -#define ADC_CH_ACCDET2_MIN 0 -#define ADC_CH_ACCDET2_MAX 2500 -#define ADC_CH_VBAT_MIN 2300 -#define ADC_CH_VBAT_MAX 4800 -#define ADC_CH_CHG_I_MIN 0 -#define ADC_CH_CHG_I_MAX 1500 -#define ADC_CH_BKBAT_MIN 0 -#define ADC_CH_BKBAT_MAX 3200 - -/* GPADC constants from AB8540 spec */ -#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ -#define ADC_CH_IBAT_MAX 6000 -#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ -#define ADC_CH_IBAT_MAX_V 60 -#define IBAT_VDROP_L (-56) /* mV */ -#define IBAT_VDROP_H 56 - -/* This is used to not lose precision when dividing to get gain and offset */ -#define CALIB_SCALE 1000 -/* - * Number of bits shift used to not lose precision - * when dividing to get ibat gain. - */ -#define CALIB_SHIFT_IBAT 20 - -/* Time in ms before disabling regulator */ -#define GPADC_AUDOSUSPEND_DELAY 1 - -#define CONVERSION_TIME 500 /* ms */ - -enum cal_channels { - ADC_INPUT_VMAIN = 0, - ADC_INPUT_BTEMP, - ADC_INPUT_VBAT, - ADC_INPUT_IBAT, - NBR_CAL_INPUTS, -}; - -/** - * struct adc_cal_data - Table for storing gain and offset for the calibrated - * ADC channels - * @gain: Gain of the ADC channel - * @offset: Offset of the ADC channel - */ -struct adc_cal_data { - s64 gain; - s64 offset; - u16 otp_calib_hi; - u16 otp_calib_lo; -}; - -/** - * struct ab8500_gpadc - AB8500 GPADC device information - * @dev: pointer to the struct device - * @node: a list of AB8500 GPADCs, hence prepared for - reentrance - * @parent: pointer to the struct ab8500 - * @ab8500_gpadc_complete: pointer to the struct completion, to indicate - * the completion of gpadc conversion - * @ab8500_gpadc_lock: structure of type mutex - * @regu: pointer to the struct regulator - * @irq_sw: interrupt number that is used by gpadc for Sw - * conversion - * @irq_hw: interrupt number that is used by gpadc for Hw - * conversion - * @cal_data array of ADC calibration data structs - */ -struct ab8500_gpadc { - struct device *dev; - struct list_head node; - struct ab8500 *parent; - struct completion ab8500_gpadc_complete; - struct mutex ab8500_gpadc_lock; - struct regulator *regu; - int irq_sw; - int irq_hw; - struct adc_cal_data cal_data[NBR_CAL_INPUTS]; -}; - -static LIST_HEAD(ab8500_gpadc_list); - -/** - * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC - * (i.e. the first GPADC in the instance list) - */ -struct ab8500_gpadc *ab8500_gpadc_get(char *name) -{ - struct ab8500_gpadc *gpadc; - - list_for_each_entry(gpadc, &ab8500_gpadc_list, node) { - if (!strcmp(name, dev_name(gpadc->dev))) - return gpadc; - } - - return ERR_PTR(-ENOENT); -} -EXPORT_SYMBOL(ab8500_gpadc_get); - -/** - * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage - */ -int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, - int ad_value) -{ - int res; - - switch (channel) { - case MAIN_CHARGER_V: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { - res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX - - ADC_CH_CHG_V_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain + - gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE; - break; - - case XTAL_TEMP: - case BAT_CTRL: - case BTEMP_BALL: - case ACC_DETECT1: - case ADC_AUX1: - case ADC_AUX2: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) { - res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX - - ADC_CH_BTEMP_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain + - gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE; - break; - - case MAIN_BAT_V: - case VBAT_TRUE_MEAS: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) { - res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX - - ADC_CH_VBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain + - gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE; - break; - - case DIE_TEMP: - res = ADC_CH_DIETEMP_MIN + - (ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case ACC_DETECT2: - res = ADC_CH_ACCDET2_MIN + - (ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case VBUS_V: - res = ADC_CH_CHG_V_MIN + - (ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case MAIN_CHARGER_C: - case USB_CHARGER_C: - res = ADC_CH_CHG_I_MIN + - (ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case BK_BAT_V: - res = ADC_CH_BKBAT_MIN + - (ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case IBAT_VIRTUAL_CHANNEL: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) { - res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX - - ADC_CH_IBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain + - gpadc->cal_data[ADC_INPUT_IBAT].offset) - >> CALIB_SHIFT_IBAT; - break; - - default: - dev_err(gpadc->dev, - "unknown channel, not possible to convert\n"); - res = -EINVAL; - break; - - } - return res; -} -EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); - -/** - * ab8500_gpadc_sw_hw_convert() - gpadc conversion - * @channel: analog channel to be converted to digital data - * @avg_sample: number of ADC sample to average - * @trig_egde: selected ADC trig edge - * @trig_timer: selected ADC trigger delay timer - * @conv_type: selected conversion type (HW or SW conversion) - * - * This function converts the selected analog i/p to digital - * data. - */ -int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) -{ - int ad_value; - int voltage; - - ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type); - - /* On failure retry a second time */ - if (ad_value < 0) - ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type); - if (ad_value < 0) { - dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", - channel); - return ad_value; - } - - voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); - if (voltage < 0) - dev_err(gpadc->dev, - "GPADC to voltage conversion failed ch: %d AD: 0x%x\n", - channel, ad_value); - - return voltage; -} -EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert); - -/** - * ab8500_gpadc_read_raw() - gpadc read - * @channel: analog channel to be read - * @avg_sample: number of ADC sample to average - * @trig_edge: selected trig edge - * @trig_timer: selected ADC trigger delay timer - * @conv_type: selected conversion type (HW or SW conversion) - * - * This function obtains the raw ADC value for an hardware conversion, - * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage() - */ -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) -{ - return ab8500_gpadc_double_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type, - NULL); -} - -int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, - int *ibat) -{ - int ret; - int looplimit = 0; - unsigned long completion_timeout; - u8 val, low_data, high_data, low_data2, high_data2; - u8 val_reg1 = 0; - unsigned int delay_min = 0; - unsigned int delay_max = 0; - u8 data_low_addr, data_high_addr; - - if (!gpadc) - return -ENODEV; - - /* check if convertion is supported */ - if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW)) - return -ENOTSUPP; - if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW)) - return -ENOTSUPP; - - mutex_lock(&gpadc->ab8500_gpadc_lock); - /* Enable VTVout LDO this is required for GPADC */ - pm_runtime_get_sync(gpadc->dev); - - /* Check if ADC is not busy, lock and proceed */ - do { - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); - if (ret < 0) - goto out; - if (!(val & GPADC_BUSY)) - break; - msleep(20); - } while (++looplimit < 10); - if (looplimit >= 10 && (val & GPADC_BUSY)) { - dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); - ret = -EINVAL; - goto out; - } - - /* Enable GPADC */ - val_reg1 |= EN_GPADC; - - /* Select the channel source and set average samples */ - switch (avg_sample) { - case SAMPLE_1: - val = channel | AVG_1; - break; - case SAMPLE_4: - val = channel | AVG_4; - break; - case SAMPLE_8: - val = channel | AVG_8; - break; - default: - val = channel | AVG_16; - break; - } - - if (conv_type == ADC_HW) { - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val); - val_reg1 |= EN_TRIG_EDGE; - if (trig_edge) - val_reg1 |= EN_FALLING; - } else - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: set avg samples failed\n"); - goto out; - } - - /* - * Enable ADC, buffering, select rising edge and enable ADC path - * charging current sense if it needed, ABB 3.0 needs some special - * treatment too. - */ - switch (channel) { - case MAIN_CHARGER_C: - case USB_CHARGER_C: - val_reg1 |= EN_BUF | EN_ICHAR; - break; - case BTEMP_BALL: - if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { - val_reg1 |= EN_BUF | BTEMP_PULL_UP; - /* - * Delay might be needed for ABB8500 cut 3.0, if not, - * remove when hardware will be availible - */ - delay_min = 1000; /* Delay in micro seconds */ - delay_max = 10000; /* large range optimises sleepmode */ - break; - } - /* Intentional fallthrough */ - default: - val_reg1 |= EN_BUF; - break; - } - - /* Write configuration to register */ - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: set Control register failed\n"); - goto out; - } - - if (delay_min != 0) - usleep_range(delay_min, delay_max); - - if (conv_type == ADC_HW) { - /* Set trigger delay timer */ - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: trig timer failed\n"); - goto out; - } - completion_timeout = 2 * HZ; - data_low_addr = AB8500_GPADC_AUTODATAL_REG; - data_high_addr = AB8500_GPADC_AUTODATAH_REG; - } else { - /* Start SW conversion */ - ret = abx500_mask_and_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, - ADC_SW_CONV, ADC_SW_CONV); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: start s/w conv failed\n"); - goto out; - } - completion_timeout = msecs_to_jiffies(CONVERSION_TIME); - data_low_addr = AB8500_GPADC_MANDATAL_REG; - data_high_addr = AB8500_GPADC_MANDATAH_REG; - } - - /* wait for completion of conversion */ - if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, - completion_timeout)) { - dev_err(gpadc->dev, - "timeout didn't receive GPADC conv interrupt\n"); - ret = -EINVAL; - goto out; - } - - /* Read the converted RAW data */ - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, data_low_addr, &low_data); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); - goto out; - } - - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, data_high_addr, &high_data); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n"); - goto out; - } - - /* Check if double convertion is required */ - if ((channel == BAT_CTRL_AND_IBAT) || - (channel == VBAT_MEAS_AND_IBAT) || - (channel == VBAT_TRUE_MEAS_AND_IBAT) || - (channel == BAT_TEMP_AND_IBAT)) { - - if (conv_type == ADC_HW) { - /* not supported */ - ret = -ENOTSUPP; - dev_err(gpadc->dev, - "gpadc_conversion: only SW double conversion supported\n"); - goto out; - } else { - /* Read the converted RAW data 2 */ - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, - &low_data2); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: read sw low data 2 failed\n"); - goto out; - } - - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, - &high_data2); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: read sw high data 2 failed\n"); - goto out; - } - if (ibat != NULL) { - *ibat = (high_data2 << 8) | low_data2; - } else { - dev_warn(gpadc->dev, - "gpadc_conversion: ibat not stored\n"); - } - - } - } - - /* Disable GPADC */ - ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL1_REG, DIS_GPADC); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); - goto out; - } - - /* Disable VTVout LDO this is required for GPADC */ - pm_runtime_mark_last_busy(gpadc->dev); - pm_runtime_put_autosuspend(gpadc->dev); - - mutex_unlock(&gpadc->ab8500_gpadc_lock); - - return (high_data << 8) | low_data; - -out: - /* - * It has shown to be needed to turn off the GPADC if an error occurs, - * otherwise we might have problem when waiting for the busy bit in the - * GPADC status register to go low. In V1.1 there wait_for_completion - * seems to timeout when waiting for an interrupt.. Not seen in V2.0 - */ - (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL1_REG, DIS_GPADC); - pm_runtime_put(gpadc->dev); - mutex_unlock(&gpadc->ab8500_gpadc_lock); - dev_err(gpadc->dev, - "gpadc_conversion: Failed to AD convert channel %d\n", channel); - return ret; -} -EXPORT_SYMBOL(ab8500_gpadc_read_raw); - -/** - * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion - * @irq: irq number - * @data: pointer to the data passed during request irq - * - * This is a interrupt service routine for gpadc conversion completion. - * Notifies the gpadc completion is completed and the converted raw value - * can be read from the registers. - * Returns IRQ status(IRQ_HANDLED) - */ -static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc) -{ - struct ab8500_gpadc *gpadc = _gpadc; - - complete(&gpadc->ab8500_gpadc_complete); - - return IRQ_HANDLED; -} - -static int otp_cal_regs[] = { - AB8500_GPADC_CAL_1, - AB8500_GPADC_CAL_2, - AB8500_GPADC_CAL_3, - AB8500_GPADC_CAL_4, - AB8500_GPADC_CAL_5, - AB8500_GPADC_CAL_6, - AB8500_GPADC_CAL_7, -}; - -static int otp4_cal_regs[] = { - AB8540_GPADC_OTP4_REG_7, - AB8540_GPADC_OTP4_REG_6, - AB8540_GPADC_OTP4_REG_5, -}; - -static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) -{ - int i; - int ret[ARRAY_SIZE(otp_cal_regs)]; - u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; - int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; - u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; - int vmain_high, vmain_low; - int btemp_high, btemp_low; - int vbat_high, vbat_low; - int ibat_high, ibat_low; - s64 V_gain, V_offset, V2A_gain, V2A_offset; - struct ab8500 *ab8500; - - ab8500 = gpadc->parent; - - /* First we read all OTP registers and store the error code */ - for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { - ret[i] = abx500_get_register_interruptible(gpadc->dev, - AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); - if (ret[i] < 0) - dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", - __func__, otp_cal_regs[i]); - } - - /* - * The ADC calibration data is stored in OTP registers. - * The layout of the calibration data is outlined below and a more - * detailed description can be found in UM0836 - * - * vm_h/l = vmain_high/low - * bt_h/l = btemp_high/low - * vb_h/l = vbat_high/low - * - * Data bits 8500/9540: - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | vm_h9 | vm_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | - * |.......|.......|.......|.......|.......|.......|.......|....... - * - * Data bits 8540: - * OTP2 - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | - * |.......|.......|.......|.......|.......|.......|.......|....... - * - * Data bits 8540: - * OTP4 - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | ib_h9 | ib_h8 | ib_h7 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | - * - * - * Ideal output ADC codes corresponding to injected input voltages - * during manufacturing is: - * - * vmain_high: Vin = 19500mV / ADC ideal code = 997 - * vmain_low: Vin = 315mV / ADC ideal code = 16 - * btemp_high: Vin = 1300mV / ADC ideal code = 985 - * btemp_low: Vin = 21mV / ADC ideal code = 16 - * vbat_high: Vin = 4700mV / ADC ideal code = 982 - * vbat_low: Vin = 2380mV / ADC ideal code = 33 - */ - - if (is_ab8540(ab8500)) { - /* Calculate gain and offset for VMAIN if all reads succeeded*/ - if (!(ret[1] < 0 || ret[2] < 0)) { - vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | - ((gpadc_cal[2] & 0xC0) >> 6)); - vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); - - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = - (u16)vmain_high; - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = - (u16)vmain_low; - - gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * - (19500 - 315) / (vmain_high - vmain_low); - gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * - 19500 - (CALIB_SCALE * (19500 - 315) / - (vmain_high - vmain_low)) * vmain_high; - } else { - gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; - } - - /* Read IBAT calibration Data */ - for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { - ret_otp4[i] = abx500_get_register_interruptible( - gpadc->dev, AB8500_OTP_EMUL, - otp4_cal_regs[i], &gpadc_otp4[i]); - if (ret_otp4[i] < 0) - dev_err(gpadc->dev, - "%s: read otp4 reg 0x%02x failed\n", - __func__, otp4_cal_regs[i]); - } - - /* Calculate gain and offset for IBAT if all reads succeeded */ - if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { - ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | - ((gpadc_otp4[1] & 0xFE) >> 1)); - ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | - ((gpadc_otp4[2] & 0xF8) >> 3)); - - gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi = - (u16)ibat_high; - gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo = - (u16)ibat_low; - - V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L) - << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); - - V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) - - (((IBAT_VDROP_H - IBAT_VDROP_L) << - CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) - * ibat_high; - /* - * Result obtained is in mV (at a scale factor), - * we need to calculate gain and offset to get mA - */ - V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/ - (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); - V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN - - ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V) - << CALIB_SHIFT_IBAT) - / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); - - gpadc->cal_data[ADC_INPUT_IBAT].gain = - V_gain * V2A_gain; - gpadc->cal_data[ADC_INPUT_IBAT].offset = - V_offset * V2A_gain + V2A_offset; - } else { - gpadc->cal_data[ADC_INPUT_IBAT].gain = 0; - } - - dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_IBAT].gain, - gpadc->cal_data[ADC_INPUT_IBAT].offset); - } else { - /* Calculate gain and offset for VMAIN if all reads succeeded */ - if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { - vmain_high = (((gpadc_cal[0] & 0x03) << 8) | - ((gpadc_cal[1] & 0x3F) << 2) | - ((gpadc_cal[2] & 0xC0) >> 6)); - vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); - - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = - (u16)vmain_high; - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = - (u16)vmain_low; - - gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * - (19500 - 315) / (vmain_high - vmain_low); - - gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * - 19500 - (CALIB_SCALE * (19500 - 315) / - (vmain_high - vmain_low)) * vmain_high; - } else { - gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; - } - } - - /* Calculate gain and offset for BTEMP if all reads succeeded */ - if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { - btemp_high = (((gpadc_cal[2] & 0x01) << 9) | - (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); - btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); - - gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high; - gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low; - - gpadc->cal_data[ADC_INPUT_BTEMP].gain = - CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); - gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - - (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) - * btemp_high; - } else { - gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0; - } - - /* Calculate gain and offset for VBAT if all reads succeeded */ - if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { - vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); - vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); - - gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high; - gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low; - - gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * - (4700 - 2380) / (vbat_high - vbat_low); - gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - - (CALIB_SCALE * (4700 - 2380) / - (vbat_high - vbat_low)) * vbat_high; - } else { - gpadc->cal_data[ADC_INPUT_VBAT].gain = 0; - } - - dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_VMAIN].gain, - gpadc->cal_data[ADC_INPUT_VMAIN].offset); - - dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_BTEMP].gain, - gpadc->cal_data[ADC_INPUT_BTEMP].offset); - - dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_VBAT].gain, - gpadc->cal_data[ADC_INPUT_VBAT].offset); -} - -#ifdef CONFIG_PM -static int ab8500_gpadc_runtime_suspend(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - - regulator_disable(gpadc->regu); - return 0; -} - -static int ab8500_gpadc_runtime_resume(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - int ret; - - ret = regulator_enable(gpadc->regu); - if (ret) - dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret); - return ret; -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int ab8500_gpadc_suspend(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - - mutex_lock(&gpadc->ab8500_gpadc_lock); - - pm_runtime_get_sync(dev); - - regulator_disable(gpadc->regu); - return 0; -} - -static int ab8500_gpadc_resume(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - int ret; - - ret = regulator_enable(gpadc->regu); - if (ret) - dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret); - - pm_runtime_mark_last_busy(gpadc->dev); - pm_runtime_put_autosuspend(gpadc->dev); - - mutex_unlock(&gpadc->ab8500_gpadc_lock); - return ret; -} -#endif - -static int ab8500_gpadc_probe(struct platform_device *pdev) -{ - int ret = 0; - struct ab8500_gpadc *gpadc; - - gpadc = devm_kzalloc(&pdev->dev, - sizeof(struct ab8500_gpadc), GFP_KERNEL); - if (!gpadc) - return -ENOMEM; - - gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); - if (gpadc->irq_sw < 0) - dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n"); - - gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); - if (gpadc->irq_hw < 0) - dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n"); - - gpadc->dev = &pdev->dev; - gpadc->parent = dev_get_drvdata(pdev->dev.parent); - mutex_init(&gpadc->ab8500_gpadc_lock); - - /* Initialize completion used to notify completion of conversion */ - init_completion(&gpadc->ab8500_gpadc_complete); - - /* Register interrupts */ - if (gpadc->irq_sw >= 0) { - ret = request_threaded_irq(gpadc->irq_sw, NULL, - ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, - "ab8500-gpadc-sw", - gpadc); - if (ret < 0) { - dev_err(gpadc->dev, - "Failed to register interrupt irq: %d\n", - gpadc->irq_sw); - goto fail; - } - } - - if (gpadc->irq_hw >= 0) { - ret = request_threaded_irq(gpadc->irq_hw, NULL, - ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, - "ab8500-gpadc-hw", - gpadc); - if (ret < 0) { - dev_err(gpadc->dev, - "Failed to register interrupt irq: %d\n", - gpadc->irq_hw); - goto fail_irq; - } - } - - /* VTVout LDO used to power up ab8500-GPADC */ - gpadc->regu = devm_regulator_get(&pdev->dev, "vddadc"); - if (IS_ERR(gpadc->regu)) { - ret = PTR_ERR(gpadc->regu); - dev_err(gpadc->dev, "failed to get vtvout LDO\n"); - goto fail_irq; - } - - platform_set_drvdata(pdev, gpadc); - - ret = regulator_enable(gpadc->regu); - if (ret) { - dev_err(gpadc->dev, "Failed to enable vtvout LDO: %d\n", ret); - goto fail_enable; - } - - pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY); - pm_runtime_use_autosuspend(gpadc->dev); - pm_runtime_set_active(gpadc->dev); - pm_runtime_enable(gpadc->dev); - - ab8500_gpadc_read_calibration_data(gpadc); - list_add_tail(&gpadc->node, &ab8500_gpadc_list); - dev_dbg(gpadc->dev, "probe success\n"); - - return 0; - -fail_enable: -fail_irq: - free_irq(gpadc->irq_sw, gpadc); - free_irq(gpadc->irq_hw, gpadc); -fail: - return ret; -} - -static int ab8500_gpadc_remove(struct platform_device *pdev) -{ - struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); - - /* remove this gpadc entry from the list */ - list_del(&gpadc->node); - /* remove interrupt - completion of Sw ADC conversion */ - if (gpadc->irq_sw >= 0) - free_irq(gpadc->irq_sw, gpadc); - if (gpadc->irq_hw >= 0) - free_irq(gpadc->irq_hw, gpadc); - - pm_runtime_get_sync(gpadc->dev); - pm_runtime_disable(gpadc->dev); - - regulator_disable(gpadc->regu); - - pm_runtime_set_suspended(gpadc->dev); - - pm_runtime_put_noidle(gpadc->dev); - - return 0; -} - -static const struct dev_pm_ops ab8500_gpadc_pm_ops = { - SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, - ab8500_gpadc_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend, - ab8500_gpadc_resume) - -}; - -static struct platform_driver ab8500_gpadc_driver = { - .probe = ab8500_gpadc_probe, - .remove = ab8500_gpadc_remove, - .driver = { - .name = "ab8500-gpadc", - .pm = &ab8500_gpadc_pm_ops, - }, -}; - -static int __init ab8500_gpadc_init(void) -{ - return platform_driver_register(&ab8500_gpadc_driver); -} -subsys_initcall_sync(ab8500_gpadc_init); - -/** - * ab8540_gpadc_get_otp() - returns OTP values - * - */ -void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, - u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, - u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h) -{ - *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo; - *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi; - *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo; - *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi; - *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo; - *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi; - *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo; - *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi; -} diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 44ca983a49a1..d94e3267c3b6 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -131,7 +131,7 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, static int sama5d3_restart(struct notifier_block *this, unsigned long mode, void *cmd) { - writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST), + writel(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST, at91_rstc_base); return NOTIFY_DONE; @@ -140,9 +140,7 @@ static int sama5d3_restart(struct notifier_block *this, unsigned long mode, static int samx7_restart(struct notifier_block *this, unsigned long mode, void *cmd) { - writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PROCRST), - at91_rstc_base); - + writel(AT91_RSTC_KEY | AT91_RSTC_PROCRST, at91_rstc_base); return NOTIFY_DONE; } diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index e341cc5c0ea6..1c18f465a245 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -269,6 +269,12 @@ static const struct of_device_id at91_shdwc_of_match[] = { }; MODULE_DEVICE_TABLE(of, at91_shdwc_of_match); +static const struct of_device_id at91_pmc_ids[] = { + { .compatible = "atmel,sama5d2-pmc" }, + { .compatible = "microchip,sam9x60-pmc" }, + { /* Sentinel. */ } +}; + static int __init at91_shdwc_probe(struct platform_device *pdev) { struct resource *res; @@ -313,7 +319,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) at91_shdwc_dt_configure(pdev); - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-pmc"); + np = of_find_matching_node(NULL, at91_pmc_ids); if (!np) { ret = -ENODEV; goto clk_disable; diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index c84a7b1caeb6..27164a1d3c7c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -629,7 +629,7 @@ config BATTERY_GAUGE_LTC2941 config AB8500_BM bool "AB8500 Battery Management Driver" - depends on AB8500_CORE && AB8500_GPADC + depends on AB8500_CORE && AB8500_GPADC && (IIO = y) help Say Y to include support for AB8500 battery management. diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 8fe81259bfd9..909f0242bacb 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -26,7 +26,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #define VTVOUT_V 1800 @@ -79,7 +79,8 @@ struct ab8500_btemp_ranges { * @bat_temp: Dispatched battery temperature in degree Celsius * @prev_bat_temp Last measured battery temperature in degree Celsius * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_btemp_ball: ADC channel for the battery ball temperature + * @adc_bat_ctrl: ADC channel for the battery control * @fg: Pointer to the struct fg * @bm: Platform specific battery management information * @btemp_psy: Structure for BTEMP specific battery properties @@ -96,7 +97,8 @@ struct ab8500_btemp { int bat_temp; int prev_bat_temp; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *btemp_ball; + struct iio_channel *bat_ctrl; struct ab8500_fg *fg; struct abx500_bm_data *bm; struct power_supply *btemp_psy; @@ -177,13 +179,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, */ static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di) { - int vbtemp; + int vbtemp, ret; static int prev; - vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL); - if (vbtemp < 0) { + ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value", + "%s ADC conversion failed, using previous value", __func__); return prev; } @@ -455,7 +457,7 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, */ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) { - int temp; + int temp, ret; static int prev; int rbat, rntc, vntc; u8 id; @@ -480,10 +482,10 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) di->bm->bat_type[id].r_to_t_tbl, di->bm->bat_type[id].n_temp_tbl_elements, rbat); } else { - vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL); - if (vntc < 0) { + ret = iio_read_channel_processed(di->btemp_ball, &vntc); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed," + "%s ADC conversion failed," " using previous value\n", __func__); return prev; } @@ -1024,7 +1026,22 @@ static int ab8500_btemp_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->btemp_ball = devm_iio_channel_get(&pdev->dev, "btemp_ball"); + if (IS_ERR(di->btemp_ball)) { + if (PTR_ERR(di->btemp_ball) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BTEMP BALL ADC channel\n"); + return PTR_ERR(di->btemp_ball); + } + di->bat_ctrl = devm_iio_channel_get(&pdev->dev, "bat_ctrl"); + if (IS_ERR(di->bat_ctrl)) { + if (PTR_ERR(di->bat_ctrl) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BAT CTRL ADC channel\n"); + return PTR_ERR(di->bat_ctrl); + } di->initialized = false; @@ -1082,6 +1099,11 @@ static int ab8500_btemp_probe(struct platform_device *pdev) /* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); + if (irq < 0) { + ret = irq; + goto free_irq; + } + ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_btemp_irq[i].name, di); @@ -1104,13 +1126,13 @@ static int ab8500_btemp_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(di->btemp_psy); - /* We also have to free all successfully registered irqs */ for (i = i - 1; i >= 0; i--) { irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); free_irq(irq, di); } + + power_supply_unregister(di->btemp_psy); free_btemp_wq: destroy_workqueue(di->btemp_wq); return ret; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index e51d0e72beea..8a0f9d769690 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -29,10 +29,10 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/mfd/abx500/ux500_chargalg.h> #include <linux/usb/otg.h> #include <linux/mutex.h> +#include <linux/iio/consumer.h> /* Charger constants */ #define NO_PW_CONN 0 @@ -233,7 +233,10 @@ struct ab8500_charger_max_usb_in_curr { * @current_stepping_sessions: * Counter for current stepping sessions * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_main_charger_v ADC channel for main charger voltage + * @adc_main_charger_c ADC channel for main charger current + * @adc_vbus_v ADC channel for USB charger voltage + * @adc_usb_charger_c ADC channel for USB charger current * @bm: Platform specific battery management information * @flags: Structure for information about events triggered * @usb_state: Structure for usb stack information @@ -283,7 +286,10 @@ struct ab8500_charger { int is_aca_rid; atomic_t current_stepping_sessions; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *adc_main_charger_v; + struct iio_channel *adc_main_charger_c; + struct iio_channel *adc_vbus_v; + struct iio_channel *adc_usb_charger_c; struct abx500_bm_data *bm; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -459,13 +465,13 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, */ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->ac.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed,\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -510,13 +516,13 @@ static int ab8500_charger_ac_cv(struct ab8500_charger *di) */ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->usb.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, VBUS_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_vbus_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -532,13 +538,13 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) */ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->usb.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_usb_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -554,13 +560,13 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) */ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->ac.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -3371,7 +3377,39 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->adc_main_charger_v = devm_iio_channel_get(&pdev->dev, + "main_charger_v"); + if (IS_ERR(di->adc_main_charger_v)) { + if (PTR_ERR(di->adc_main_charger_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger voltage\n"); + return PTR_ERR(di->adc_main_charger_v); + } + di->adc_main_charger_c = devm_iio_channel_get(&pdev->dev, + "main_charger_c"); + if (IS_ERR(di->adc_main_charger_c)) { + if (PTR_ERR(di->adc_main_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger current\n"); + return PTR_ERR(di->adc_main_charger_c); + } + di->adc_vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); + if (IS_ERR(di->adc_vbus_v)) { + if (PTR_ERR(di->adc_vbus_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger voltage\n"); + return PTR_ERR(di->adc_vbus_v); + } + di->adc_usb_charger_c = devm_iio_channel_get(&pdev->dev, + "usb_charger_c"); + if (IS_ERR(di->adc_usb_charger_c)) { + if (PTR_ERR(di->adc_usb_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger current\n"); + return PTR_ERR(di->adc_usb_charger_c); + } /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); @@ -3556,6 +3594,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); + if (irq < 0) { + ret = irq; + goto free_irq; + } + ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_charger_irq[i].name, di); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 6fc4bc30644c..c3912ee9eb99 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -32,7 +32,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #include <linux/kernel.h> #define MILLI_TO_MICRO 1000 @@ -182,7 +182,7 @@ struct inst_curr_result_list { * @bat_cap: Structure for battery capacity specific parameters * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @main_bat_v: ADC channel for the main battery voltage * @bm: Platform specific battery management information * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm @@ -224,7 +224,7 @@ struct ab8500_fg { struct ab8500_fg_battery_capacity bat_cap; struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *main_bat_v; struct abx500_bm_data *bm; struct power_supply *fg_psy; struct workqueue_struct *fg_wq; @@ -829,13 +829,13 @@ exit: */ static int ab8500_fg_bat_voltage(struct ab8500_fg *di) { - int vbat; + int vbat, ret; static int prev; - vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V); - if (vbat < 0) { + ret = iio_read_channel_processed(di->main_bat_v, &vbat); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value\n", + "%s ADC conversion failed, using previous value\n", __func__); return prev; } @@ -3066,7 +3066,14 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + di->main_bat_v = devm_iio_channel_get(&pdev->dev, "main_bat_v"); + if (IS_ERR(di->main_bat_v)) { + if (PTR_ERR(di->main_bat_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get main battery ADC channel\n"); + return PTR_ERR(di->main_bat_v); + } psy_cfg.supplied_to = supply_interface; psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); @@ -3151,6 +3158,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register primary interrupt handlers */ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); + if (irq < 0) { + ret = irq; + goto free_irq_th; + } + ret = request_irq(irq, ab8500_fg_irq_th[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_fg_irq_th[i].name, di); @@ -3158,7 +3170,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret != 0) { dev_err(di->dev, "failed to request %s IRQ %d: %d\n", ab8500_fg_irq_th[i].name, irq, ret); - goto free_irq; + goto free_irq_th; } dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq_th[i].name, irq, ret); @@ -3166,6 +3178,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register threaded interrupt handler */ irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + if (irq < 0) { + ret = irq; + goto free_irq_th; + } + ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_fg_irq_bh[0].name, di); @@ -3173,7 +3190,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret != 0) { dev_err(di->dev, "failed to request %s IRQ %d: %d\n", ab8500_fg_irq_bh[0].name, irq, ret); - goto free_irq; + goto free_irq_th; } dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq_bh[0].name, irq, ret); @@ -3212,15 +3229,17 @@ static int ab8500_fg_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(di->fg_psy); - /* We also have to free all registered irqs */ - for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + free_irq(irq, di); +free_irq_th: + while (--i >= 0) { + /* Last assignment of i from primary interrupt handlers */ irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); free_irq(irq, di); } - irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); - free_irq(irq, di); + + power_supply_unregister(di->fg_psy); free_inst_curr_wq: destroy_workqueue(di->fg_wq); return ret; diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c index 23757fb10479..e6e37d4f20e4 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/abx500_chargalg.c @@ -354,13 +354,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) if (di->chg_info.charger_type & USB_CHG) { return di->usb_chg->ops.check_enable(di->usb_chg, - di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, - di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); } else if ((di->chg_info.charger_type & AC_CHG) && !(di->ac_chg->external)) { return di->ac_chg->ops.check_enable(di->ac_chg, - di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, - di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); } return 0; } diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index dc4c316eff81..5f0a5722b19e 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -48,6 +48,8 @@ #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) +#define AXP813_BC_EN BIT(0) + /* * Note do not raise the debounce time, we must report Vusb high within * 100ms otherwise we get Vbus errors in musb. @@ -495,6 +497,12 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return -EINVAL; } + if (power->axp20x_id == AXP813_ID) { + /* Enable USB Battery Charging specification detection */ + regmap_update_bits(axp20x->regmap, AXP288_BC_GLOBAL, + AXP813_BC_EN, AXP813_BC_EN); + } + psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = power; diff --git a/drivers/power/supply/bd70528-charger.c b/drivers/power/supply/bd70528-charger.c index 1bb32b7226d7..b8e1ec106627 100644 --- a/drivers/power/supply/bd70528-charger.c +++ b/drivers/power/supply/bd70528-charger.c @@ -741,3 +741,4 @@ module_platform_driver(bd70528_power); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("BD70528 power-supply driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-power"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 61d6447d1966..6e9392901b0a 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -33,8 +33,6 @@ #include <linux/iio/types.h> #include <linux/mfd/motorola-cpcap.h> -#include <asm/div64.h> - /* * Register bit defines for CPCAP_REG_BPEOL. Some of these seem to * map to MC13783UG.pdf "Table 5-19. Register 13, Power Control 0" @@ -52,6 +50,26 @@ #define CPCAP_REG_BPEOL_BIT_BATTDETEN BIT(1) /* Enable battery detect */ #define CPCAP_REG_BPEOL_BIT_EOLSEL BIT(0) /* BPDET = 0, EOL = 1 */ +/* + * Register bit defines for CPCAP_REG_CCC1. These seem similar to the twl6030 + * coulomb counter registers rather than the mc13892 registers. Both twl6030 + * and mc13892 set bits 2 and 1 to reset and clear registers. But mc13892 + * sets bit 0 to start the coulomb counter while twl6030 sets bit 0 to stop + * the coulomb counter like cpcap does. So for now, we use the twl6030 style + * naming for the registers. + */ +#define CPCAP_REG_CCC1_ACTIVE_MODE1 BIT(4) /* Update rate */ +#define CPCAP_REG_CCC1_ACTIVE_MODE0 BIT(3) /* Update rate */ +#define CPCAP_REG_CCC1_AUTOCLEAR BIT(2) /* Resets sample registers */ +#define CPCAP_REG_CCC1_CAL_EN BIT(1) /* Clears after write in 1s */ +#define CPCAP_REG_CCC1_PAUSE BIT(0) /* Stop counters, allow write */ +#define CPCAP_REG_CCC1_RESET_MASK (CPCAP_REG_CCC1_AUTOCLEAR | \ + CPCAP_REG_CCC1_CAL_EN) + +#define CPCAP_REG_CCCC2_RATE1 BIT(5) +#define CPCAP_REG_CCCC2_RATE0 BIT(4) +#define CPCAP_REG_CCCC2_ENABLE BIT(3) + #define CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS 250 enum { @@ -64,6 +82,7 @@ enum { enum cpcap_battery_irq_action { CPCAP_BATTERY_IRQ_ACTION_NONE, + CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE, CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW, CPCAP_BATTERY_IRQ_ACTION_POWEROFF, }; @@ -76,15 +95,16 @@ struct cpcap_interrupt_desc { }; struct cpcap_battery_config { - int ccm; int cd_factor; struct power_supply_info info; + struct power_supply_battery_info bat; }; struct cpcap_coulomb_counter_data { s32 sample; /* 24 or 32 bits */ s32 accumulator; s16 offset; /* 9 bits */ + s16 integrator; /* 13 or 16 bits */ }; enum cpcap_battery_state { @@ -110,6 +130,7 @@ struct cpcap_battery_ddata { struct power_supply *psy; struct cpcap_battery_config config; struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR]; + u32 cc_lsb; /* μAms per LSB */ atomic_t active; int status; u16 vendor; @@ -217,41 +238,17 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata, s16 offset, u32 divider) { s64 acc; - u64 tmp; - int avg_current; - u32 cc_lsb; if (!divider) return 0; - switch (ddata->vendor) { - case CPCAP_VENDOR_ST: - cc_lsb = 95374; /* μAms per LSB */ - break; - case CPCAP_VENDOR_TI: - cc_lsb = 91501; /* μAms per LSB */ - break; - default: - return -EINVAL; - } - acc = accumulator; - acc = acc - ((s64)sample * offset); - cc_lsb = (cc_lsb * ddata->config.cd_factor) / 1000; + acc -= (s64)sample * offset; + acc *= ddata->cc_lsb; + acc *= -1; + acc = div_s64(acc, divider); - if (acc >= 0) - tmp = acc; - else - tmp = acc * -1; - - tmp = tmp * cc_lsb; - do_div(tmp, divider); - avg_current = tmp; - - if (acc >= 0) - return -avg_current; - else - return avg_current; + return acc; } /* 3600000μAms = 1μAh */ @@ -293,12 +290,13 @@ static int cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, struct cpcap_coulomb_counter_data *ccd) { - u16 buf[7]; /* CPCAP_REG_CC1 to CCI */ + u16 buf[7]; /* CPCAP_REG_CCS1 to CCI */ int error; ccd->sample = 0; ccd->accumulator = 0; ccd->offset = 0; + ccd->integrator = 0; /* Read coulomb counter register range */ error = regmap_bulk_read(ddata->reg, CPCAP_REG_CCS1, @@ -323,6 +321,12 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, ccd->offset = buf[4]; ccd->offset = sign_extend32(ccd->offset, 9); + /* Integrator register CPCAP_REG_CCI */ + if (ddata->vendor == CPCAP_VENDOR_TI) + ccd->integrator = sign_extend32(buf[6], 13); + else + ccd->integrator = (s16)buf[6]; + return cpcap_battery_cc_to_uah(ddata, ccd->sample, ccd->accumulator, @@ -336,31 +340,28 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, static int cpcap_battery_cc_get_avg_current(struct cpcap_battery_ddata *ddata) { int value, acc, error; - s32 sample = 1; + s32 sample; s16 offset; - if (ddata->vendor == CPCAP_VENDOR_ST) - sample = 4; - /* Coulomb counter integrator */ error = regmap_read(ddata->reg, CPCAP_REG_CCI, &value); if (error) return error; - if ((ddata->vendor == CPCAP_VENDOR_TI) && (value > 0x2000)) - value = value | 0xc000; - - acc = (s16)value; + if (ddata->vendor == CPCAP_VENDOR_TI) { + acc = sign_extend32(value, 13); + sample = 1; + } else { + acc = (s16)value; + sample = 4; + } - /* Coulomb counter sample time */ + /* Coulomb counter calibration offset */ error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); if (error) return error; - if (value < 0x200) - offset = value; - else - offset = value | 0xfc00; + offset = sign_extend32(value, 9); return cpcap_battery_cc_to_ua(ddata, sample, acc, offset); } @@ -369,8 +370,8 @@ static bool cpcap_battery_full(struct cpcap_battery_ddata *ddata) { struct cpcap_battery_state_data *state = cpcap_battery_latest(ddata); - /* Basically anything that measures above 4347000 is full */ - if (state->voltage >= (ddata->config.info.voltage_max_design - 4000)) + if (state->voltage >= + (ddata->config.bat.constant_charge_voltage_max_uv - 18000)) return true; return false; @@ -417,6 +418,7 @@ static enum power_supply_property cpcap_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, @@ -475,6 +477,9 @@ static int cpcap_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = ddata->config.info.voltage_min_design; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = ddata->config.bat.constant_charge_voltage_max_uv; + break; case POWER_SUPPLY_PROP_CURRENT_AVG: sample = latest->cc.sample - previous->cc.sample; if (!sample) { @@ -540,6 +545,69 @@ static int cpcap_battery_get_property(struct power_supply *psy, return 0; } +static int cpcap_battery_update_charger(struct cpcap_battery_ddata *ddata, + int const_charge_voltage) +{ + union power_supply_propval prop; + union power_supply_propval val; + struct power_supply *charger; + int error; + + charger = power_supply_get_by_name("usb"); + if (!charger) + return -ENODEV; + + error = power_supply_get_property(charger, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &prop); + if (error) + return error; + + /* Allow charger const voltage lower than battery const voltage */ + if (const_charge_voltage > prop.intval) + return 0; + + val.intval = const_charge_voltage; + + return power_supply_set_property(charger, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &val); +} + +static int cpcap_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct cpcap_battery_ddata *ddata = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + if (val->intval < ddata->config.info.voltage_min_design) + return -EINVAL; + if (val->intval > ddata->config.info.voltage_max_design) + return -EINVAL; + + ddata->config.bat.constant_charge_voltage_max_uv = val->intval; + + return cpcap_battery_update_charger(ddata, val->intval); + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return 1; + default: + return 0; + } +} + static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) { struct cpcap_battery_ddata *ddata = data; @@ -560,14 +628,19 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) latest = cpcap_battery_latest(ddata); switch (d->action) { + case CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE: + dev_info(ddata->dev, "Coulomb counter calibration done\n"); + break; case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: if (latest->current_ua >= 0) - dev_warn(ddata->dev, "Battery low at 3.3V!\n"); + dev_warn(ddata->dev, "Battery low at %imV!\n", + latest->voltage / 1000); break; case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: - if (latest->current_ua >= 0) { + if (latest->current_ua >= 0 && latest->voltage <= 3200000) { dev_emerg(ddata->dev, - "Battery empty at 3.1V, powering off\n"); + "Battery empty at %imV, powering off\n", + latest->voltage / 1000); orderly_poweroff(true); } break; @@ -609,7 +682,9 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, d->name = name; d->irq = irq; - if (!strncmp(name, "lowbph", 6)) + if (!strncmp(name, "cccal", 5)) + d->action = CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE; + else if (!strncmp(name, "lowbph", 6)) d->action = CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW; else if (!strncmp(name, "lowbpl", 6)) d->action = CPCAP_BATTERY_IRQ_ACTION_POWEROFF; @@ -635,6 +710,9 @@ static int cpcap_battery_init_interrupts(struct platform_device *pdev, return error; } + /* Enable calibration interrupt if already available in dts */ + cpcap_battery_init_irq(pdev, ddata, "cccal"); + /* Enable low battery interrupts for 3.3V high and 3.1V low */ error = regmap_update_bits(ddata->reg, CPCAP_REG_BPEOL, 0xffff, @@ -676,6 +754,60 @@ out_err: return error; } +/* Calibrate coulomb counter */ +static int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata) +{ + int error, ccc1, value; + unsigned long timeout; + + error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &ccc1); + if (error) + return error; + + timeout = jiffies + msecs_to_jiffies(6000); + + /* Start calibration */ + error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1, + 0xffff, + CPCAP_REG_CCC1_CAL_EN); + if (error) + goto restore; + + while (time_before(jiffies, timeout)) { + error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &value); + if (error) + goto restore; + + if (!(value & CPCAP_REG_CCC1_CAL_EN)) + break; + + error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); + if (error) + goto restore; + + msleep(300); + } + + /* Read calibration offset from CCM */ + error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); + if (error) + goto restore; + + dev_info(ddata->dev, "calibration done: 0x%04x\n", value); + +restore: + if (error) + dev_err(ddata->dev, "%s: error %i\n", __func__, error); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1, + 0xffff, ccc1); + if (error) + dev_err(ddata->dev, "%s: restore error %i\n", + __func__, error); + + return error; +} + /* * Based on the values from Motorola mapphone Linux kernel. In the * the Motorola mapphone Linux kernel tree the value for pm_cd_factor @@ -687,12 +819,12 @@ out_err: * at 3078000. The device will die around 2743000. */ static const struct cpcap_battery_config cpcap_battery_default_data = { - .ccm = 0x3ff, .cd_factor = 0x3cc, .info.technology = POWER_SUPPLY_TECHNOLOGY_LION, .info.voltage_max_design = 4351000, .info.voltage_min_design = 3100000, .info.charge_full_design = 1740000, + .bat.constant_charge_voltage_max_uv = 4200000, }; #ifdef CONFIG_OF @@ -741,12 +873,19 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (error) return error; - platform_set_drvdata(pdev, ddata); + switch (ddata->vendor) { + case CPCAP_VENDOR_ST: + ddata->cc_lsb = 95374; /* μAms per LSB */ + break; + case CPCAP_VENDOR_TI: + ddata->cc_lsb = 91501; /* μAms per LSB */ + break; + default: + return -EINVAL; + } + ddata->cc_lsb = (ddata->cc_lsb * ddata->config.cd_factor) / 1000; - error = regmap_update_bits(ddata->reg, CPCAP_REG_CCM, - 0xffff, ddata->config.ccm); - if (error) - return error; + platform_set_drvdata(pdev, ddata); error = cpcap_battery_init_interrupts(pdev, ddata); if (error) @@ -760,11 +899,13 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (!psy_desc) return -ENOMEM; - psy_desc->name = "battery", - psy_desc->type = POWER_SUPPLY_TYPE_BATTERY, - psy_desc->properties = cpcap_battery_props, - psy_desc->num_properties = ARRAY_SIZE(cpcap_battery_props), - psy_desc->get_property = cpcap_battery_get_property, + psy_desc->name = "battery"; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->properties = cpcap_battery_props; + psy_desc->num_properties = ARRAY_SIZE(cpcap_battery_props); + psy_desc->get_property = cpcap_battery_get_property; + psy_desc->set_property = cpcap_battery_set_property; + psy_desc->property_is_writeable = cpcap_battery_property_is_writeable; psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = ddata; @@ -779,6 +920,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) atomic_set(&ddata->active, 1); + error = cpcap_battery_calibrate(ddata); + if (error) + return error; + return 0; } diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 74258c7fe17d..c0d452e3dc8b 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -120,6 +120,13 @@ enum { CPCAP_CHARGER_IIO_NR, }; +enum { + CPCAP_CHARGER_DISCONNECTED, + CPCAP_CHARGER_DETECTING, + CPCAP_CHARGER_CHARGING, + CPCAP_CHARGER_DONE, +}; + struct cpcap_charger_ddata { struct device *dev; struct regmap *reg; @@ -138,6 +145,8 @@ struct cpcap_charger_ddata { atomic_t active; int status; + int state; + int voltage; }; struct cpcap_interrupt_desc { @@ -153,6 +162,7 @@ struct cpcap_charger_ints_state { bool chrg_se1b; bool rvrs_mode; + bool chrgcurr2; bool chrgcurr1; bool vbusvld; @@ -162,24 +172,26 @@ struct cpcap_charger_ints_state { static enum power_supply_property cpcap_charger_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; +/* No battery always shows temperature of -40000 */ static bool cpcap_charger_battery_found(struct cpcap_charger_ddata *ddata) { struct iio_channel *channel; - int error, value; + int error, temperature; channel = ddata->channels[CPCAP_CHARGER_IIO_BATTDET]; - error = iio_read_channel_raw(channel, &value); + error = iio_read_channel_processed(channel, &temperature); if (error < 0) { dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); return false; } - return value == 1; + return temperature > -20000 && temperature < 60000; } static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata) @@ -224,6 +236,9 @@ static int cpcap_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_STATUS: val->intval = ddata->status; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = ddata->voltage; + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (ddata->status == POWER_SUPPLY_STATUS_CHARGING) val->intval = cpcap_charger_get_charge_voltage(ddata) * @@ -248,6 +263,83 @@ static int cpcap_charger_get_property(struct power_supply *psy, return 0; } +static int cpcap_charger_match_voltage(int voltage) +{ + switch (voltage) { + case 0 ... 4100000 - 1: return 3800000; + case 4100000 ... 4120000 - 1: return 4100000; + case 4120000 ... 4150000 - 1: return 4120000; + case 4150000 ... 4170000 - 1: return 4150000; + case 4170000 ... 4200000 - 1: return 4170000; + case 4200000 ... 4230000 - 1: return 4200000; + case 4230000 ... 4250000 - 1: return 4230000; + case 4250000 ... 4270000 - 1: return 4250000; + case 4270000 ... 4300000 - 1: return 4270000; + case 4300000 ... 4330000 - 1: return 4300000; + case 4330000 ... 4350000 - 1: return 4330000; + case 4350000 ... 4380000 - 1: return 4350000; + case 4380000 ... 4400000 - 1: return 4380000; + case 4400000 ... 4420000 - 1: return 4400000; + case 4420000 ... 4440000 - 1: return 4420000; + case 4440000: return 4440000; + default: return 0; + } +} + +static int +cpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata) +{ + union power_supply_propval prop; + struct power_supply *battery; + int voltage = ddata->voltage; + int error; + + battery = power_supply_get_by_name("battery"); + if (battery) { + error = power_supply_get_property(battery, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &prop); + if (!error) + voltage = prop.intval; + } + + return voltage; +} + +static int cpcap_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent); + int voltage, batvolt; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + voltage = cpcap_charger_match_voltage(val->intval); + batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata); + if (voltage > batvolt) + voltage = batvolt; + ddata->voltage = voltage; + schedule_delayed_work(&ddata->detect_work, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return 1; + default: + return 0; + } +} + static void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata, bool enabled) { @@ -422,6 +514,7 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata, s->chrg_se1b = val & BIT(13); s->rvrs_mode = val & BIT(6); + s->chrgcurr2 = val & BIT(5); s->chrgcurr1 = val & BIT(4); s->vbusvld = val & BIT(3); @@ -434,6 +527,79 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata, return 0; } +static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata, + int state) +{ + const char *status; + + if (state > CPCAP_CHARGER_DONE) { + dev_warn(ddata->dev, "unknown state: %i\n", state); + + return; + } + + ddata->state = state; + + switch (state) { + case CPCAP_CHARGER_DISCONNECTED: + status = "DISCONNECTED"; + break; + case CPCAP_CHARGER_DETECTING: + status = "DETECTING"; + break; + case CPCAP_CHARGER_CHARGING: + status = "CHARGING"; + break; + case CPCAP_CHARGER_DONE: + status = "DONE"; + break; + default: + return; + } + + dev_dbg(ddata->dev, "state: %s\n", status); +} + +static int cpcap_charger_voltage_to_regval(int voltage) +{ + int offset; + + switch (voltage) { + case 0 ... 4100000 - 1: + return 0; + case 4100000 ... 4200000 - 1: + offset = 1; + break; + case 4200000 ... 4300000 - 1: + offset = 0; + break; + case 4300000 ... 4380000 - 1: + offset = -1; + break; + case 4380000 ... 4440000: + offset = -2; + break; + default: + return 0; + } + + return ((voltage - 4100000) / 20000) + offset; +} + +static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata, + int state, unsigned long delay) +{ + int error; + + error = cpcap_charger_set_state(ddata, 0, 0, 0); + if (error) + return; + + cpcap_charger_update_state(ddata, state); + power_supply_changed(ddata->usb); + schedule_delayed_work(&ddata->detect_work, delay); +} + static void cpcap_usb_detect(struct work_struct *work) { struct cpcap_charger_ddata *ddata; @@ -447,24 +613,67 @@ static void cpcap_usb_detect(struct work_struct *work) if (error) return; + /* Just init the state if a charger is connected with no chrg_det set */ + if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) { + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DETECTING); + + return; + } + + /* + * If battery voltage is higher than charge voltage, it may have been + * charged to 4.35V by Android. Try again in 10 minutes. + */ + if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) { + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, + HZ * 60 * 10); + + return; + } + + /* Throttle chrgcurr2 interrupt for charger done and retry */ + switch (ddata->state) { + case CPCAP_CHARGER_CHARGING: + if (s.chrgcurr2) + break; + if (s.chrgcurr1 && s.vbusvld) { + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DONE, + HZ * 5); + return; + } + break; + case CPCAP_CHARGER_DONE: + if (!s.chrgcurr2) + break; + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, + HZ * 5); + return; + default: + break; + } + if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) && s.chrgcurr1) { int max_current; + int vchrg; if (cpcap_charger_battery_found(ddata)) max_current = CPCAP_REG_CRM_ICHRG_1A596; else max_current = CPCAP_REG_CRM_ICHRG_0A532; + vchrg = cpcap_charger_voltage_to_regval(ddata->voltage); error = cpcap_charger_set_state(ddata, - CPCAP_REG_CRM_VCHRG_4V35, + CPCAP_REG_CRM_VCHRG(vchrg), max_current, 0); if (error) goto out_err; + cpcap_charger_update_state(ddata, CPCAP_CHARGER_CHARGING); } else { error = cpcap_charger_set_state(ddata, 0, 0, 0); if (error) goto out_err; + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DISCONNECTED); } power_supply_changed(ddata->usb); @@ -524,7 +733,7 @@ static const char * const cpcap_charger_irqs[] = { "chrg_det", "rvrs_chrg", /* REG_INT1 */ - "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr1", "vbusvld", + "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", /* REG_INT_3 */ "battdetb", @@ -596,6 +805,8 @@ static const struct power_supply_desc cpcap_charger_usb_desc = { .properties = cpcap_charger_props, .num_properties = ARRAY_SIZE(cpcap_charger_props), .get_property = cpcap_charger_get_property, + .set_property = cpcap_charger_set_property, + .property_is_writeable = cpcap_charger_property_is_writeable, }; #ifdef CONFIG_OF @@ -625,6 +836,7 @@ static int cpcap_charger_probe(struct platform_device *pdev) return -ENOMEM; ddata->dev = &pdev->dev; + ddata->voltage = 4200000; ddata->reg = dev_get_regmap(ddata->dev->parent, NULL); if (!ddata->reg) diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index c3cad2b6daba..65c23ef6408d 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -33,6 +33,8 @@ static int battery_present = 1; /* true */ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; static int battery_capacity = 50; static int battery_voltage = 3300; +static int battery_charge_counter = -1000; +static int battery_current = 1600; static bool module_initialized; @@ -100,6 +102,9 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_NOW: val->intval = battery_capacity; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = battery_charge_counter; + break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = 100; @@ -114,6 +119,10 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = battery_voltage; break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = battery_current; + break; default: pr_info("%s: some properties deliberately report errors.\n", __func__); @@ -135,6 +144,7 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -144,6 +154,8 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, }; static char *test_power_ac_supplied_to[] = { @@ -447,6 +459,36 @@ static int param_set_battery_voltage(const char *key, #define param_get_battery_voltage param_get_int +static int param_set_battery_charge_counter(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_charge_counter = tmp; + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); + return 0; +} + +#define param_get_battery_charge_counter param_get_int + +static int param_set_battery_current(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_current = tmp; + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); + return 0; +} + +#define param_get_battery_current param_get_int + static const struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, @@ -487,6 +529,16 @@ static const struct kernel_param_ops param_ops_battery_voltage = { .get = param_get_battery_voltage, }; +static const struct kernel_param_ops param_ops_battery_charge_counter = { + .set = param_set_battery_charge_counter, + .get = param_get_battery_charge_counter, +}; + +static const struct kernel_param_ops param_ops_battery_current = { + .set = param_set_battery_current, + .get = param_get_battery_current, +}; + #define param_check_ac_online(name, p) __param_check(name, p, void); #define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); @@ -495,6 +547,8 @@ static const struct kernel_param_ops param_ops_battery_voltage = { #define param_check_battery_health(name, p) __param_check(name, p, void); #define param_check_battery_capacity(name, p) __param_check(name, p, void); #define param_check_battery_voltage(name, p) __param_check(name, p, void); +#define param_check_battery_charge_counter(name, p) __param_check(name, p, void); +#define param_check_battery_current(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); @@ -525,6 +579,13 @@ MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); module_param(battery_voltage, battery_voltage, 0644); MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)"); +module_param(battery_charge_counter, battery_charge_counter, 0644); +MODULE_PARM_DESC(battery_charge_counter, + "battery charge counter (microampere-hours)"); + +module_param(battery_current, battery_current, 0644); +MODULE_PARM_DESC(battery_current, "battery current (milliampere)"); + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500/ab8500-gpadc.h b/include/linux/mfd/abx500/ab8500-gpadc.h deleted file mode 100644 index 836c944abe2e..000000000000 --- a/include/linux/mfd/abx500/ab8500-gpadc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2010 ST-Ericsson SA - * - * Author: Arun R Murthy <arun.murthy@stericsson.com> - * Author: Daniel Willerud <daniel.willerud@stericsson.com> - * Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com> - */ - -#ifndef _AB8500_GPADC_H -#define _AB8500_GPADC_H - -/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2 - * and ADCHwSel[4:0] in GPADCCtrl3 ) */ -#define BAT_CTRL 0x01 -#define BTEMP_BALL 0x02 -#define MAIN_CHARGER_V 0x03 -#define ACC_DETECT1 0x04 -#define ACC_DETECT2 0x05 -#define ADC_AUX1 0x06 -#define ADC_AUX2 0x07 -#define MAIN_BAT_V 0x08 -#define VBUS_V 0x09 -#define MAIN_CHARGER_C 0x0A -#define USB_CHARGER_C 0x0B -#define BK_BAT_V 0x0C -#define DIE_TEMP 0x0D -#define USB_ID 0x0E -#define XTAL_TEMP 0x12 -#define VBAT_TRUE_MEAS 0x13 -#define BAT_CTRL_AND_IBAT 0x1C -#define VBAT_MEAS_AND_IBAT 0x1D -#define VBAT_TRUE_MEAS_AND_IBAT 0x1E -#define BAT_TEMP_AND_IBAT 0x1F - -/* Virtual channel used only for ibat convertion to ampere - * Battery current conversion (ibat) cannot be requested as a single conversion - * but it is always in combination with other input requests - */ -#define IBAT_VIRTUAL_CHANNEL 0xFF - -#define SAMPLE_1 1 -#define SAMPLE_4 4 -#define SAMPLE_8 8 -#define SAMPLE_16 16 -#define RISING_EDGE 0 -#define FALLING_EDGE 1 - -/* Arbitrary ADC conversion type constants */ -#define ADC_SW 0 -#define ADC_HW 1 - -struct ab8500_gpadc; - -struct ab8500_gpadc *ab8500_gpadc_get(char *name); -int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); -static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) -{ - return ab8500_gpadc_sw_hw_convert(gpadc, channel, - SAMPLE_16, 0, 0, ADC_SW); -} - -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); -int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, - int *ibat); -int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, - u8 channel, int ad_value); -void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, - u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, - u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h); - -#endif /* _AB8500_GPADC_H */ |