From 941bfaf2dc352b2ba2cd7e50c55843e4585aaffb Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Tue, 28 Feb 2017 15:47:52 -0800 Subject: power: supply: qcom: remove unused drivers qpnp-fg and qpnp-smbcharger are not supported anymore. Remove them. Change-Id: Icc010cd77d9d1e839a6dfa681b65e19b8978fb56 Signed-off-by: Abhijeet Dharmapurikar --- .../bindings/power/supply/qcom/qpnp-fg.txt | 275 - .../bindings/power/supply/qcom/qpnp-smbcharger.txt | 394 - arch/arm64/configs/msm-perf_defconfig | 2 - arch/arm64/configs/msm_defconfig | 2 - drivers/power/supply/qcom/Kconfig | 18 - drivers/power/supply/qcom/Makefile | 2 - drivers/power/supply/qcom/qpnp-fg.c | 7051 ---------------- drivers/power/supply/qcom/qpnp-smbcharger.c | 8472 -------------------- 8 files changed, 16216 deletions(-) delete mode 100644 Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt delete mode 100644 Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt delete mode 100644 drivers/power/supply/qcom/qpnp-fg.c delete mode 100644 drivers/power/supply/qcom/qpnp-smbcharger.c diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt deleted file mode 100644 index f6a7a1ba3005..000000000000 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt +++ /dev/null @@ -1,275 +0,0 @@ -QTI's QPNP PMIC Fuel Gauge Device - -QPNP PMIC FG provides interface to clients to read properties related -to the battery. Its main function is to retrieve the State of Charge (SOC), -a 0-100 percentage representing the amount of charge left in the battery. - -There are two required peripherals in the FG driver, both implemented as -subnodes in the example. These peripherals must not be disabled if the FG -device is to enabled: - -- qcom,fg-soc : The main FG device. Supports battery fuel gauge controls and - sensors. -- qcom,fg-batt : The FG battery device supports interrupts and controls with - respect to the state of the connected battery.For example: the - peripheral informs the driver if the battery has been identified - by the fuel gauge based on a given battery resistance range. - -Optionally ADC nodes can be added -- qcom,revid-tp-rev: A subnode with a register address for the TP_REV register - in the REVID peripheral. This is used to apply workarounds that - may depend on the trim program. -- qcom,fg-adc-vbat : A subnode with a register address for the FG_ADC_USR - peripheral which is used mainly for battery current limiting (BCL). - This node maps out the VBAT reading register which allows to have - a +/- 32 mV accurate reading of VBAT. -- qcom,fg-adc-ibat : A subnode with a register address for the FG_ADC_USR - peripheral which is used mainly for battery current limiting (BCL). - This node maps out the IBAT current reading register which allows - to have a +/- 32 mA accurate reading of IBAT. - -Parent node required properties: -- compatible : should be "qcom,qpnp-fg" for the FG driver. -- qcom,pmic-revid : Should specify the phandle of PMIC - revid module. This is used to identify - the PMIC subtype. - -Parent node optional properties: -- qcom,warm-bat-decidegc: Warm battery temperature in decidegC. -- qcom,cool-bat-decidegc: Cool battery temperature in decidegC. -- qcom,hot-bat-decidegc: Hot battery temperature in decidegC. -- qcom,cold-bat-decidegc: Cold battery temperature in decidegC. -- qcom,cold-hot-jeita-hysteresis: A tuple of 2. Index[0] is cold - hysteresis and index[1] is hot - hysterisis(in decidegC). -- qcom,ext-sense-type: Current sense channel used by the FG. - Set this to use external rsense. -- qcom,thermal-coefficients: Byte array of thermal coefficients for - reading battery thermistor. This should - be exactly 6 bytes in length. - Example: [01 02 03 04 05 06] -- qcom,resume-soc: soc to resume charging in percentage. -- qcom,resume-soc-raw: soc to resume charging in the scale of - [0-255]. This overrides qcom,resume-soc - if defined. -- qcom,hold-soc-while-full: A boolean property that when defined - holds SOC at 100% when the battery is - full. -- qcom,bcl-lm-threshold-ma: BCL LPM to MPM mode transition threshold - in milliAmpere. -- qcom,bcl-mh-threshold-ma: BCL MPM to HPM mode transition threshold - in milliAmpere. -- qcom,use-otp-profile: Specify this flag to avoid RAM loading - any battery profile. -- qcom,sw-rbias-control: Boolean property which defines whether - the Rbias needs to be controlled by - software. If this is not set, it will - be controlled by hardware (default). -- qcom,fg-iterm-ma: Battery current at which the fuel gauge - will try to scale 100% towards. When - the charge current goes above this, the - SoC should be at 100%. -- qcom,fg-chg-iterm-ma: Battery current at which the fuel gauge - will issue end of charge if the charger - is configured to use the fuel gauge - ADCs for end of charge detection. This - property is in milliamps and should be - positive (e.g. 100mA to terminate at - -100mA). -- qcom,irq-volt-empty-mv: The voltage threshold that the empty - soc interrupt will be triggered. When - the empty soc interrupt fires, battery - soc will be pulled to 0 and the - userspace will be notified via the - power supply framework. The userspace - will read 0% soc and immediately - shutdown. -- qcom,fg-cutoff-voltage-mv: The voltage where the fuel gauge will - steer the SOC to be zero. For example, - if the cutoff voltage is set to 3400mv, - the fuel gauge will try to count SoC so - that the battery SoC will be 0 when it - is 3400mV. -- qcom,fg-vbat-estimate-diff-mv: If the estimated voltage based on SoC - and battery current/resistance differs - from the actual voltage by more than - this amount, the fuel gauge will - redo the first SoC estimate when the - driver probes. -- qcom,fg-delta-soc: How many percent the monotonic SoC must - change before a new delta_soc interrupt - is asserted. If this value is raised - above 3-4, some period workarounds may - not function well, so it's best to - leave this at 1 or 2%. -- qcom,fg-vbatt-low-threshold: Voltage (in mV) which upon set will be - used for configuring the low battery - voltage threshold. Interrupt will be - asserted and handled based upon - this. If this property is not specified, - low battery voltage threshold will be - configured to 4200 mV. -- qcom,cycle-counter-en: Boolean property which enables the cycle - counter feature. If this property is - present, then the following properties - to specify low and high soc thresholds - should be defined. -- qcom,capacity-learning-on: A boolean property to have the fuel - gauge driver attempt to learn the - battery capacity when charging. Takes - precedence over capacity-estimation-on. -- qcom,capacity-learning-feedback: A boolean property to have the fuel - gauge driver to feedback the learned - capacity into the capacity learning - algorithm. This has to be used only if - the property "qcom,capacity-learning-on" - is specified. -- qcom,cl-max-increment-deciperc: The maximum percent that the capacity - can rise as the result of a single - charge cycle. This property corresponds - to .1% increments. -- qcom,cl-max-decrement-deciperc: The maximum percent that the capacity - can fall as the result of a single - charge cycle. This property corresponds - to .1% decrements. -- qcom,cl-max-temp-decidegc: Above this temperature, capacity - learning will be canceled. -- qcom,cl-mix-temp-decidegc: Below this temperature, capacity - learning will be canceled. -- qcom,cl-max-start-soc: The battery soc has to be below this - value at the start of a charge cycle - for capacity learning to be run. -- qcom,cl-vbat-est-thr-uv: The maximum difference between the - battery voltage shadow and the current - predicted voltage in uV to initiate - capacity learning. -- qcom,capacity-estimation-on: A boolean property to have the fuel - gauge driver attempt to estimate the - battery capacity using battery - resistance. -- qcom,aging-eval-current-ma: Current used to evaluate battery aging. - This value should be around the steady - state current drawn from the battery - when the phone is low on battery. -- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for configuring - constant charge (CC) to constant - voltage (CV) setpoint in FG upon - which the battery EOC status will - be determined. This value should be - 10 mV less than the float voltage - configured in the charger. - This property should only be specified - if "qcom,autoadjust-vfloat" property is - specified in the charger driver to - ensure a proper operation. -- qcom,bad-battery-detection-enable: A boolean property to enable the fuel - gauge driver to detect the damaged battery - when the safety-timer expires by using the - coulomb count. -- qcom,fg-therm-delay-us: The time in microseconds to delay battery - thermistor biasing. -- qcom,esr-pulse-tuning-en: A boolean property to enable ESR pulse - tuning feature. If this is enabled, - ESR pulse extraction will be disabled - when state of charge (SOC) is less than - 2%. It will be enabled back when SOC - gets above 2%. In addition, for SOC - between 2% and 5%, ESR pulse timing - settings will be different from default. - Once SOC crosses 5%, ESR pulse timings - will be restored back to default. - -qcom,fg-soc node required properties: -- reg : offset and length of the PMIC peripheral register map. -- interrupts : the interrupt mappings. - The format should be - . -- interrupt-names : names for the mapped fg soc interrupts - The following interrupts are required: - 0: high-soc - 1: low-soc - 2: full-soc - 3: empty-soc - 4: delta-soc - 5: first-est-done - 6: sw-fallbk-ocv - 7: sw-fallbk-new-batt - -qcom,fg-memif node required properties: -- reg : offset and length of the PMIC peripheral register map. -- interrupts : the interrupt mappings. - The format should be - . -- interrupt-names : names for the mapped fg adc interrupts - The following interrupts are required: - 0: mem-avail - -Example: -pmi8994_fg: qcom,fg { - compatible = "qcom,qpnp-fg"; - #address-cells = <1>; - #size-cells = <1>; - status = "disabled"; - qcom,pmic-revid = <&pmi8994_revid>; - - qcom,fg-soc@4000 { - reg = <0x4000 0x100>; - interrupts = <0x2 0x40 0x0>, - <0x2 0x40 0x1>, - <0x2 0x40 0x2>, - <0x2 0x40 0x3>, - <0x2 0x40 0x4>, - <0x2 0x40 0x5>, - <0x2 0x40 0x6>, - <0x2 0x40 0x7>; - - interrupt-names = "high-soc", - "low-soc", - "full-soc", - "empty-soc", - "delta-soc", - "first-est-done", - "sw-fallbk-ocv", - "sw-fallbk-new-batt"; - }; - - qcom,fg-batt@4100 { - reg = <0x4100 0x100>; - interrupts = <0x2 0x41 0x0>, - <0x2 0x41 0x1>, - <0x2 0x41 0x2>, - <0x2 0x41 0x3>, - <0x2 0x41 0x4>, - <0x2 0x41 0x5>, - <0x2 0x41 0x6>, - <0x2 0x41 0x7>; - - interrupt-names = "soft-cold", - "soft-hot", - "vbatt-low", - "batt-ided", - "batt-id-req", - "batt-unknown", - "batt-missing", - "batt-match"; - }; - - qcom,fg-adc-vbat@4254 { - reg = <0x4254 0x1>; - }; - - qcom,fg-adc-ibat@4255 { - reg = <0x4255 0x1>; - }; - - qcom,fg-memif@4400 { - reg = <0x4400 0x100>; - interrupts = <0x2 0x44 0x0>, - <0x2 0x44 0x1>; - - interrupt-names = "mem-avail", - "data-rcvry-sug"; - - qcom,cold-hot-jeita-hysteresis = <30 50>; - }; -}; diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt deleted file mode 100644 index efd64cd90878..000000000000 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt +++ /dev/null @@ -1,394 +0,0 @@ -QPNP SMB Battery Charger - -QPNP SMB Charger is a single-cell switching mode battery charger. It can charge -the battery and power the system via the USB and AC adapter input. - -The QPNP SMB Charger interfaces via the SPMI bus. - -There are six different peripherals adding the following functionality. -Each of these peripherals are implemented as subnodes in the example at the -end of this file. - -- qcom,chgr: Supports charging control and status - reporting. -- qcom,bat-if: Battery status reporting such as presence, - temperature reporting and voltage collapse - protection. -- qcom,usb-chgpth: USB charge path detection and input current - limiting configuration. -- qcom,dc-chgpth: DC charge path detection and input current - limiting configuration. -- qcom,chg-misc: Miscellaneous features such as watchdog timers - and SYSOK pin control -- qcom,chg-otg: OTG configuration control. - -Parent node required properties: -- compatible: Must be "qcom,qpnp-smbcharger" -- #address-cells: Must be <1> -- #size-cells: Must be <1> -- qcom,pmic-revid: Should specify the phandle of PMIC - revid module. This is used to identify - the PMIC subtype. - - - -Sub node required properties: -- reg: The SPMI address for this peripheral -- interrupts: Specifies the interrupt associated with the peripheral. -- interrupt-names: Specifies the interrupt names for the peripheral. Every - available interrupt needs to have an associated name - with it to indentify its purpose. - - The following lists each subnode and their corresponding - required interrupt names: - - qcom,chgr: - - chg-tcc-thr: Triggers on charge completion. - - chg-taper-thr: Triggers on the taper charge - transtion. - - chg-inhibit: Notifies on battery voltage - being too high to resume - charging. - - chg-p2f-thr: Triggers on transitioning from - precharge to fastcharge. - - chg-rechg-thr: Triggers on battery voltage - falling below the resume - threshold. - - qcom,bat-if: - - batt-hot: Triggers on battery temperature - hitting the hot threshold. - Charging stops. - - batt-warm: Triggers on battery temperature - hitting the warm threshold. - Charging current is reduced. - - batt-cool: Triggers on battery temperature - hitting the cool threshold. - Charging current is reduced - - batt-cold: Triggers on battery temperature - hitting the cold threshold. - Charging stops. - - batt-missing: Battery missing status - interrupt. - - batt-low: Triggers on battery voltage - falling across a low threshold. - - qcom,usb-chgpth: - - usbin-uv: USB input voltage falls below a - valid threshold. - - usbin-src-det: USB automatic source detection - finishes. - - qcom,dc-chgpth: - - dcin-uv: DC input voltage falls below a - valid threshold. - - qcom,chgr-misc: - - wdog-timeout-mins: Charger watchdog timer - interrupt. - - temp-shutdown: Triggers when charger goes - overtemp and causes a shutdown. - - power-ok: Triggers when the charger - switcher turns on or off. - -Regulator Subnodes: -- qcom,smbcharger-boost-otg A subnode for a regulator device that turns on - the charger boost for OTG operation. -- qcom,smbcharger-external-otg A subnode for a regulator device that switches - off charging and the USB input charge path - in order to allow an external regulator to - operate. This can be used in place of the - qcom,smbcharger-boost-otg if an external boost - is available. - -Regulator Sub node required properties: -- regulator-name A name string for the regulator in question - -Optional Properties: -- qcom,battery-psy-name The name of the main battery power supply that - the charger will register. Failing to define - this property will default the name to - "battery". -- qcom,bms-psy-name The psy name to use for reporting battery - capacity. If left unspecified the capacity uses - a preprogrammed default value of 50. -- qcom,float-voltage-mv Float Voltage in mV - the maximum voltage up - to which the battery is charged. Supported - range 3600mV to 4500mV -- qcom,float-voltage-comp Specifies the JEITA float voltage compensation. - Value ranges from 0 to 63. -- qcom,fastchg-current-ma Specifies the fast charge current in mA. Supported - range is from 300mA to 3000mA. -- qcom,fastchg-current-comp Specifies the fast charge current compensation in - mA. Supported values are 250, 700, 900 and 1200mA. -- qcom,charging-timeout-mins Maximum duration in minutes that a single - charge cycle may last. Supported values are: - 0, 192, 384, 768, and 1536. A value of 0 - means that no charge cycle timeout is used and - charging can continue indefinitely. -- qcom,precharging-timeout-mins Maximum duration in minutes that a single - precharge cycle may last. Supported values - are: 0, 24, 48, 96, 192. A value of 0 means - that no precharge cycle timeout is used and - charging can continue indefinitely. Note that - the qcom,charging-timeout-mins property must - be specified in order for this to take effect. -- qcom,dc-psy-type The type of charger connected to the DC path. - Can be "Mains", "Wireless" or "Wipower" -- qcom,dc-psy-ma The current in mA dc path can support. Must be - specified if dc-psy-type is specified. Valid - range 300mA to 2000mA. -- qcom,dcin-vadc The phandle to pmi8994 voltage adc. The ADC is - used to get notifications when the DCIN voltage - crosses a programmed min/max threshold. This is - used to make configurations for optimized power - draw for Wipower. -- qcom,wipower-div2-ilim-map -- qcom,wipower-pt-ilim-map -- qcom,wipower-default-ilim-map - Array of 5 elements to indicate the voltage ranges and their corresponding - current limits. The 5 elements with index [0..4] are: - [0] => voltage_low in uV - [1] => voltage_high in uV - [2] => current limit for pass through in mA - [3] => current limit for div2 mode dcin low voltage in mA - [4] => current limit for div2 mode dcin high voltage in mA - The div2 and pt tables indicate the current limits - to use when Wipower is operating in divide_by_2 mode - and pass through mode respectively. - The default table is used when the voltage ranges - are beyond the ones specified in the mapping table. - Note that if dcin-vadc or any of these mapping - tables are not specified, dynamic dcin input - is disabled. -- qcom,charging-disabled Set this if charging should be disabled in the - build by default. -- qcom,resume-delta-mv Specifies the minimum voltage drop in - millivolts below the float voltage that is - required in order to initiate a new charging - cycle. Supported values are: 50, 100, 200 and - 300mV. -- qcom,chg-inhibit-en Boolean that indicates whether the charge inhibit - feature needs to be enabled. If this is not set, - charge inhibit feature is disabled by default. -- qcom,chg-inhibit-fg Indicates if the recharge threshold source has - to be Fuel gauge ADC. If this is not set, it - will be analog sensor by default. -- qcom,bmd-algo-disabled Indicates if the battery missing detection - algorithm is disabled. If this node is present - SMB uses the THERM pin for battery missing - detection. -- qcom,charge-unknown-battery Boolean that indicates whether an unknown - battery without a matching profile will be - charged. If this is not set, if the fuel gauge - does not recognize the battery based on its - battery ID, the charger will not start - charging. -- qcom,bmd-pin-src A string that indicates the source pin for the - battery missind detection. This can be either: - - "bpd_none" - battery is considered always present - - "bpd_id" - battery id pin is used - - "bpd_thm" - battery therm pin is used - - "bpd_thm_id" - both pins are used (battery is - considered missing if either pin is - floating). -- qcom,iterm-ma Specifies the termination current to indicate - end-of-charge. Possible values in mA: - 50, 100, 150, 200, 250, 300, 500, 600. -- qcom,iterm-disabled Disables the termination current feature. This - is a boolean property. -- otg-parent-supply A phandle to an external boost regulator for - OTG if it exists. -- qcom,thermal-mitigation: Array of input current limit values for - different system thermal mitigation levels. - This should be a flat array that denotates the - maximum charge current in mA for each thermal - level. -- qcom,rparasitics-uohm: The parasitic resistance of the board following - the line from the battery connectors through - vph_power. This is used to calculate maximum - available current of the battery. -- qcom,vled-max-uv: The maximum input voltage of the flash leds. - This is used to calculate maximum available - current of the battery. -- qcom,autoadjust-vfloat A boolean property that when set, makes the - driver automatically readjust vfloat using the - fuel gauge ADC readings to make charging more - accurate. -- qcom,jeita-temp-hard-limit property when present will enable or disable - the jeita temperature hard limit based on the - value 1 or 0. Specify 0 if the jeita temp hard - limit needs to be disabled. If it is not present, - jeita temperature hard limit will be based on what - the bootloader had set earlier. -- qcom,low-volt-dcin: A boolean property which upon set will enable the - AICL deglitch configuration dynamically. This needs - to be set if the DCIN supply is going to be less - than or equal to 5V. -- qcom,force-aicl-rerun: A boolean property which upon set will enable the - AICL rerun by default along with the deglitch time - configured to long interval (20 ms). Also, specifying - this property will not adjust the AICL deglitch time - dynamically for handling the battery over-voltage - oscillations when the charger is headroom limited. -- qcom,aicl-rerun-period-s If force-aicl-rerun is on, this property dictates - how often aicl is reran in seconds. Possible values - are 45, 90, 180, and 360. -- qcom,ibat-ocp-threshold-ua Maximum current before the battery will trigger - overcurrent protection. Use the recommended - battery pack value minus some margin. -- qcom,soft-vfloat-comp-disabled Set this property when the battery is - powered via external source and could - go above the float voltage. -- qcom,parallel-usb-min-current-ma Minimum current drawn by the primary - charger before enabling the parallel - charger if one exists. Do not define - this property if no parallel chargers - exist. -- qcom,parallel-usb-9v-min-current-ma Minimum current drawn by the primary - charger before enabling the parallel - charger if one exists. This property - applies only for 9V chargers. -- qcom,parallel-allowed-lowering-ma Acceptable current drop from the initial limit - to keep parallel charger activated. If the - charger current reduces beyond this threshold - parallel charger is disabled. Must be specified - if parallel charger is used. -- qcom,parallel-main-chg-fcc-percent Percentage of the fast charge current allotted to the - main charger when parallel charging is enabled and - operational. If this property is not defined, the - driver defaults to a 50%/50% split between the main - and parallel charger. -- qcom,parallel-main-chg-icl-percent Percentage of the input current allotted to the - main charger when parallel charging is enabled and - operational. If this property is not defined, the - driver defaults to a 60%/40% split between the main - and parallel charger. -- qcom,battery-data Points to the phandle of node which - contains the battery-profiles supported - by the charger/FG. -- qcom,chg-led-support A bool property to support the charger led feature. -- qcom,chg-led-sw-controls A bool property to allow the software to control - the charger led without a valid charger. -- qcom,skip-usb-notification A boolean property to be used when usb gets present - and type from other means. Especially true on - liquid hardware, where usb presence is detected based on GPIO. -- qcom,skip-usb-suspend-for-fake-battery A boolean property to skip - suspending USB path for fake - battery. -- qcom,vchg_sns-vadc Phandle of the VADC node. -- qcom,vchg-adc-channel-id The ADC channel to which the VCHG is routed. - -Example: - qcom,qpnp-smbcharger { - compatible = "qcom,qpnp-smbcharger"; - #address-cells = <1>; - #size-cells = <1>; - - qcom,iterm-ma = <100>; - qcom,float-voltage-mv = <4200>; - qcom,resume-delta-mv = <100>; - qcom,bmd-pin-src = "bpd_thm_id"; - qcom,dc-psy-type = "Mains"; - qcom,dc-psy-ma = <1500>; - qcom,bms-psy-name = "bms"; - qcom,battery-psy-name = "battery"; - qcom,thermal-mitigation = <1500 700 600 325>; - qcom,vchg_sns-vadc = <&pmi8950_vadc>; - qcom,vchg-adc-channel-id = <3>; - - qcom,chgr@1000 { - reg = <0x1000 0x100>; - interrupts = <0x2 0x10 0x0>, - <0x2 0x10 0x1>, - <0x2 0x10 0x2>, - <0x2 0x10 0x3>, - <0x2 0x10 0x4>, - <0x2 0x10 0x5>, - <0x2 0x10 0x6>, - <0x2 0x10 0x7>; - - interrupt-names = "chg-error", - "chg-inhibit", - "chg-prechg-sft", - "chg-complete-chg-sft", - "chg-p2f-thr", - "chg-rechg-thr", - "chg-taper-thr", - "chg-tcc-thr"; - }; - - qcom,otg@1100 { - reg = <0x1100 0x100>; - }; - - qcom,bat-if@1200 { - reg = <0x1200 0x100>; - interrupts = <0x2 0x12 0x0>, - <0x2 0x12 0x1>, - <0x2 0x12 0x2>, - <0x2 0x12 0x3>, - <0x2 0x12 0x4>, - <0x2 0x12 0x5>, - <0x2 0x12 0x6>, - <0x2 0x12 0x7>; - - interrupt-names = "batt-hot", - "batt-warm", - "batt-cold", - "batt-cool", - "batt-ov", - "batt-low", - "batt-missing", - "batt-term-missing"; - }; - - qcom,usb-chgpth@1300 { - reg = <0x1300 0x100>; - interrupts = <0x2 0x13 0x0>, - <0x2 0x13 0x1>, - <0x2 0x13 0x2>, - <0x2 0x13 0x3>, - <0x2 0x13 0x4>, - <0x2 0x13 0x5>, - <0x2 0x13 0x6>; - - interrupt-names = "usbin-uv", - "usbin-ov", - "usbin-src-det", - "otg-fail", - "otg-oc", - "aicl-done", - "usbid-change"; - }; - - qcom,dc-chgpth@1400 { - reg = <0x1400 0x100>; - interrupts = <0x2 0x14 0x0>, - <0x2 0x14 0x1>; - - interrupt-names = "dcin-uv", - "dcin-ov"; - }; - - qcom,chgr-misc@1600 { - reg = <0x1600 0x100>; - interrupts = <0x2 0x16 0x0>, - <0x2 0x16 0x1>, - <0x2 0x16 0x2>, - <0x2 0x16 0x3>, - <0x2 0x16 0x4>, - <0x2 0x16 0x5>; - - interrupt-names = "power-ok", - "temp-shutdown", - "wdog-timeout", - "flash-fail", - "otst2", - "otst3"; - }; - }; diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 03c5bc89b6f5..35d1c42e233e 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -327,8 +327,6 @@ CONFIG_POWER_RESET_QCOM=y CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y -CONFIG_QPNP_SMBCHARGER=y -CONFIG_QPNP_FG=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index b4ddd9a2bed5..3a3dc7ed7c84 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -314,8 +314,6 @@ CONFIG_POWER_RESET_QCOM=y CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y -CONFIG_QPNP_SMBCHARGER=y -CONFIG_QPNP_FG=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index b919c688e627..47b201738672 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -1,23 +1,5 @@ menu "Qualcomm Technologies Inc Charger and Fuel Gauge support" -config QPNP_SMBCHARGER - tristate "QPNP SMB Charger driver" - depends on MFD_SPMI_PMIC - help - Say Y here to enable the dual path switch mode battery charger which - supports USB detection and battery charging up to 3A. - The driver also offers relevant information to userspace via the - power supply framework. - -config QPNP_FG - tristate "QPNP fuel gauge driver" - depends on MFD_SPMI_PMIC - help - Say Y here to enable the Fuel Gauge driver. This adds support for - battery fuel gauging and state of charge of battery connected to the - fuel gauge. The state of charge is reported through a BMS power - supply property and also sends uevents when the capacity is updated. - config QPNP_FG_GEN3 tristate "QPNP GEN3 fuel gauge driver" depends on MFD_SPMI_PMIC diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index a3aff858c190..dfa83f2304b2 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -1,5 +1,3 @@ -obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o -obj-$(CONFIG_QPNP_FG) += qpnp-fg.o obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c deleted file mode 100644 index cfd2f64a9bb8..000000000000 --- a/drivers/power/supply/qcom/qpnp-fg.c +++ /dev/null @@ -1,7051 +0,0 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) "FG: %s: " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register offsets */ - -/* Interrupt offsets */ -#define INT_RT_STS(base) (base + 0x10) -#define INT_EN_CLR(base) (base + 0x16) - -/* SPMI Register offsets */ -#define SOC_MONOTONIC_SOC 0x09 -#define SOC_BOOT_MOD 0x50 -#define SOC_RESTART 0x51 - -#define REG_OFFSET_PERP_SUBTYPE 0x05 - -/* RAM register offsets */ -#define RAM_OFFSET 0x400 - -/* Bit/Mask definitions */ -#define FULL_PERCENT 0xFF -#define MAX_TRIES_SOC 5 -#define MA_MV_BIT_RES 39 -#define MSB_SIGN BIT(7) -#define IBAT_VBAT_MASK 0x7F -#define NO_OTP_PROF_RELOAD BIT(6) -#define REDO_FIRST_ESTIMATE BIT(3) -#define RESTART_GO BIT(0) -#define THERM_DELAY_MASK 0xE0 - -/* SUBTYPE definitions */ -#define FG_SOC 0x9 -#define FG_BATT 0xA -#define FG_ADC 0xB -#define FG_MEMIF 0xC - -#define QPNP_FG_DEV_NAME "qcom,qpnp-fg" -#define MEM_IF_TIMEOUT_MS 5000 -#define BUCKET_COUNT 8 -#define BUCKET_SOC_PCT (256 / BUCKET_COUNT) - -#define BCL_MA_TO_ADC(_current, _adc_val) { \ - _adc_val = (u8)((_current) * 100 / 976); \ -} - -/* Debug Flag Definitions */ -enum { - FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */ - FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */ - FG_IRQS = BIT(2), /* Show interrupts */ - FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */ - FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */ - FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */ - FG_STATUS = BIT(6), /* Show FG status changes */ - FG_AGING = BIT(7), /* Show FG aging algorithm */ -}; - -/* PMIC REVISIONS */ -#define REVID_RESERVED 0 -#define REVID_VARIANT 1 -#define REVID_ANA_MAJOR 2 -#define REVID_DIG_MAJOR 3 - -enum dig_major { - DIG_REV_1 = 0x1, - DIG_REV_2 = 0x2, - DIG_REV_3 = 0x3, -}; - -enum pmic_subtype { - PMI8994 = 10, - PMI8950 = 17, - PMI8996 = 19, - PMI8937 = 55, -}; - -enum wa_flags { - IADC_GAIN_COMP_WA = BIT(0), - USE_CC_SOC_REG = BIT(1), - PULSE_REQUEST_WA = BIT(2), - BCL_HI_POWER_FOR_CHGLED_WA = BIT(3) -}; - -enum current_sense_type { - INTERNAL_CURRENT_SENSE, - EXTERNAL_CURRENT_SENSE, -}; - -struct fg_mem_setting { - u16 address; - u8 offset; - int value; -}; - -struct fg_mem_data { - u16 address; - u8 offset; - unsigned int len; - int value; -}; - -struct fg_learning_data { - int64_t cc_uah; - int64_t learned_cc_uah; - int init_cc_pc_val; - bool active; - bool feedback_on; - struct mutex learning_lock; - ktime_t time_stamp; - /* configuration properties */ - int max_start_soc; - int max_increment; - int max_decrement; - int min_temp; - int max_temp; - int vbat_est_thr_uv; -}; - -struct fg_rslow_data { - u8 rslow_cfg; - u8 rslow_thr; - u8 rs_to_rslow[2]; - u8 rslow_comp[4]; - uint32_t chg_rs_to_rslow; - uint32_t chg_rslow_comp_c1; - uint32_t chg_rslow_comp_c2; - uint32_t chg_rslow_comp_thr; - bool active; - struct mutex lock; -}; - -struct fg_cyc_ctr_data { - bool en; - bool started[BUCKET_COUNT]; - u16 count[BUCKET_COUNT]; - u8 last_soc[BUCKET_COUNT]; - int id; - struct mutex lock; -}; - -struct fg_iadc_comp_data { - u8 dfl_gain_reg[2]; - bool gain_active; - int64_t dfl_gain; -}; - -struct fg_cc_soc_data { - int init_sys_soc; - int init_cc_soc; - int full_capacity; - int delta_soc; -}; - -/* FG_MEMIF setting index */ -enum fg_mem_setting_index { - FG_MEM_SOFT_COLD = 0, - FG_MEM_SOFT_HOT, - FG_MEM_HARD_COLD, - FG_MEM_HARD_HOT, - FG_MEM_RESUME_SOC, - FG_MEM_BCL_LM_THRESHOLD, - FG_MEM_BCL_MH_THRESHOLD, - FG_MEM_TERM_CURRENT, - FG_MEM_CHG_TERM_CURRENT, - FG_MEM_IRQ_VOLT_EMPTY, - FG_MEM_CUTOFF_VOLTAGE, - FG_MEM_VBAT_EST_DIFF, - FG_MEM_DELTA_SOC, - FG_MEM_BATT_LOW, - FG_MEM_THERM_DELAY, - FG_MEM_SETTING_MAX, -}; - -/* FG_MEMIF data index */ -enum fg_mem_data_index { - FG_DATA_BATT_TEMP = 0, - FG_DATA_OCV, - FG_DATA_VOLTAGE, - FG_DATA_CURRENT, - FG_DATA_BATT_ESR, - FG_DATA_BATT_ESR_COUNT, - FG_DATA_BATT_SOC, - FG_DATA_CC_CHARGE, - FG_DATA_VINT_ERR, - FG_DATA_CPRED_VOLTAGE, - /* values below this only gets read once per profile reload */ - FG_DATA_BATT_ID, - FG_DATA_BATT_ID_INFO, - FG_DATA_MAX, -}; - -#define SETTING(_idx, _address, _offset, _value) \ - [FG_MEM_##_idx] = { \ - .address = _address, \ - .offset = _offset, \ - .value = _value, \ - } \ - -static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = { - /* ID Address, Offset, Value*/ - SETTING(SOFT_COLD, 0x454, 0, 100), - SETTING(SOFT_HOT, 0x454, 1, 400), - SETTING(HARD_COLD, 0x454, 2, 50), - SETTING(HARD_HOT, 0x454, 3, 450), - SETTING(RESUME_SOC, 0x45C, 1, 0), - SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50), - SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752), - SETTING(TERM_CURRENT, 0x40C, 2, 250), - SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250), - SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100), - SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200), - SETTING(VBAT_EST_DIFF, 0x000, 0, 30), - SETTING(DELTA_SOC, 0x450, 3, 1), - SETTING(BATT_LOW, 0x458, 0, 4200), - SETTING(THERM_DELAY, 0x4AC, 3, 0), -}; - -#define DATA(_idx, _address, _offset, _length, _value) \ - [FG_DATA_##_idx] = { \ - .address = _address, \ - .offset = _offset, \ - .len = _length, \ - .value = _value, \ - } \ - -static struct fg_mem_data fg_data[FG_DATA_MAX] = { - /* ID Address, Offset, Length, Value*/ - DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL), - DATA(OCV, 0x588, 3, 2, -EINVAL), - DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL), - DATA(CURRENT, 0x5CC, 3, 2, -EINVAL), - DATA(BATT_ESR, 0x554, 2, 2, -EINVAL), - DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL), - DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL), - DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL), - DATA(VINT_ERR, 0x560, 0, 4, -EINVAL), - DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL), - DATA(BATT_ID, 0x594, 1, 1, -EINVAL), - DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL), -}; - -static int fg_debug_mask; -module_param_named( - debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR -); - -static int fg_sense_type = -EINVAL; -static int fg_restart; - -static int fg_est_dump; -module_param_named( - first_est_dump, fg_est_dump, int, S_IRUSR | S_IWUSR -); - -static char *fg_batt_type; -module_param_named( - battery_type, fg_batt_type, charp, S_IRUSR | S_IWUSR -); - -static int fg_sram_update_period_ms = 30000; -module_param_named( - sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR -); - -struct fg_irq { - int irq; - unsigned long disabled; -}; - -enum fg_soc_irq { - HIGH_SOC, - LOW_SOC, - FULL_SOC, - EMPTY_SOC, - DELTA_SOC, - FIRST_EST_DONE, - SW_FALLBK_OCV, - SW_FALLBK_NEW_BATT, - FG_SOC_IRQ_COUNT, -}; - -enum fg_batt_irq { - JEITA_SOFT_COLD, - JEITA_SOFT_HOT, - VBATT_LOW, - BATT_IDENTIFIED, - BATT_ID_REQ, - BATTERY_UNKNOWN, - BATT_MISSING, - BATT_MATCH, - FG_BATT_IRQ_COUNT, -}; - -enum fg_mem_if_irq { - FG_MEM_AVAIL, - TA_RCVRY_SUG, - FG_MEM_IF_IRQ_COUNT, -}; - -enum fg_batt_aging_mode { - FG_AGING_NONE, - FG_AGING_ESR, - FG_AGING_CC, -}; - -enum register_type { - MEM_INTF_CFG, - MEM_INTF_CTL, - MEM_INTF_ADDR_LSB, - MEM_INTF_RD_DATA0, - MEM_INTF_WR_DATA0, - MAX_ADDRESS, -}; - -struct register_offset { - u16 address[MAX_ADDRESS]; -}; - -static struct register_offset offset[] = { - [0] = { - /* CFG CTL LSB RD0 WD0 */ - .address = {0x40, 0x41, 0x42, 0x4C, 0x48}, - }, - [1] = { - /* CFG CTL LSB RD0 WD0 */ - .address = {0x50, 0x51, 0x61, 0x67, 0x63}, - }, -}; - -#define MEM_INTF_CFG(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_CFG]) -#define MEM_INTF_CTL(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_CTL]) -#define MEM_INTF_ADDR_LSB(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB]) -#define MEM_INTF_RD_DATA0(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0]) -#define MEM_INTF_WR_DATA0(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0]) - -struct fg_wakeup_source { - struct wakeup_source source; - unsigned long enabled; -}; - -static void fg_stay_awake(struct fg_wakeup_source *source) -{ - if (!__test_and_set_bit(0, &source->enabled)) { - __pm_stay_awake(&source->source); - pr_debug("enabled source %s\n", source->source.name); - } -} - -static void fg_relax(struct fg_wakeup_source *source) -{ - if (__test_and_clear_bit(0, &source->enabled)) { - __pm_relax(&source->source); - pr_debug("disabled source %s\n", source->source.name); - } -} - -#define THERMAL_COEFF_N_BYTES 6 -struct fg_chip { - struct device *dev; - struct platform_device *pdev; - struct regmap *regmap; - u8 pmic_subtype; - u8 pmic_revision[4]; - u8 revision[4]; - u16 soc_base; - u16 batt_base; - u16 mem_base; - u16 vbat_adc_addr; - u16 ibat_adc_addr; - u16 tp_rev_addr; - u32 wa_flag; - atomic_t memif_user_cnt; - struct fg_irq soc_irq[FG_SOC_IRQ_COUNT]; - struct fg_irq batt_irq[FG_BATT_IRQ_COUNT]; - struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT]; - struct completion sram_access_granted; - struct completion sram_access_revoked; - struct completion batt_id_avail; - struct completion first_soc_done; - struct power_supply *bms_psy; - struct power_supply_desc bms_psy_d; - struct mutex rw_lock; - struct mutex sysfs_restart_lock; - struct delayed_work batt_profile_init; - struct work_struct dump_sram; - struct work_struct status_change_work; - struct work_struct cycle_count_work; - struct work_struct battery_age_work; - struct work_struct update_esr_work; - struct work_struct set_resume_soc_work; - struct work_struct rslow_comp_work; - struct work_struct sysfs_restart_work; - struct work_struct init_work; - struct work_struct charge_full_work; - struct work_struct gain_comp_work; - struct work_struct bcl_hi_power_work; - struct power_supply *batt_psy; - struct power_supply *usb_psy; - struct power_supply *dc_psy; - struct fg_wakeup_source memif_wakeup_source; - struct fg_wakeup_source profile_wakeup_source; - struct fg_wakeup_source empty_check_wakeup_source; - struct fg_wakeup_source resume_soc_wakeup_source; - struct fg_wakeup_source gain_comp_wakeup_source; - struct fg_wakeup_source capacity_learning_wakeup_source; - bool first_profile_loaded; - struct fg_wakeup_source update_temp_wakeup_source; - struct fg_wakeup_source update_sram_wakeup_source; - bool fg_restarting; - bool profile_loaded; - bool use_otp_profile; - bool battery_missing; - bool power_supply_registered; - bool sw_rbias_ctrl; - bool use_thermal_coefficients; - bool esr_strict_filter; - bool soc_empty; - bool charge_done; - bool resume_soc_lowered; - bool vbat_low_irq_enabled; - bool charge_full; - bool hold_soc_while_full; - bool input_present; - bool otg_present; - bool safety_timer_expired; - bool bad_batt_detection_en; - bool bcl_lpm_disabled; - bool charging_disabled; - struct delayed_work update_jeita_setting; - struct delayed_work update_sram_data; - struct delayed_work update_temp_work; - struct delayed_work check_empty_work; - char *batt_profile; - u8 thermal_coefficients[THERMAL_COEFF_N_BYTES]; - u32 cc_cv_threshold_mv; - unsigned int batt_profile_len; - unsigned int batt_max_voltage_uv; - const char *batt_type; - const char *batt_psy_name; - unsigned long last_sram_update_time; - unsigned long last_temp_update_time; - int64_t ocv_coeffs[12]; - int64_t cutoff_voltage; - int evaluation_current; - int ocv_junction_p1p2; - int ocv_junction_p2p3; - int nom_cap_uah; - int actual_cap_uah; - int status; - int prev_status; - int health; - enum fg_batt_aging_mode batt_aging_mode; - /* capacity learning */ - struct fg_learning_data learning_data; - struct alarm fg_cap_learning_alarm; - struct work_struct fg_cap_learning_work; - struct fg_cc_soc_data sw_cc_soc_data; - /* rslow compensation */ - struct fg_rslow_data rslow_comp; - /* cycle counter */ - struct fg_cyc_ctr_data cyc_ctr; - /* iadc compensation */ - struct fg_iadc_comp_data iadc_comp_data; - /* interleaved memory access */ - u16 *offset; - bool ima_supported; - bool init_done; - /* jeita hysteresis */ - bool jeita_hysteresis_support; - bool batt_hot; - bool batt_cold; - int cold_hysteresis; - int hot_hysteresis; - /* ESR pulse tuning */ - struct fg_wakeup_source esr_extract_wakeup_source; - struct work_struct esr_extract_config_work; - bool esr_extract_disabled; - bool imptr_pulse_slow_en; - bool esr_pulse_tune_en; -}; - -/* FG_MEMIF DEBUGFS structures */ -#define ADDR_LEN 4 /* 3 byte address + 1 space character */ -#define CHARS_PER_ITEM 3 /* Format is 'XX ' */ -#define ITEMS_PER_LINE 4 /* 4 data items per line */ -#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1) -#define MAX_REG_PER_TRANSACTION (8) - -static const char *DFS_ROOT_NAME = "fg_memif"; -static const mode_t DFS_MODE = S_IRUSR | S_IWUSR; -static const char *default_batt_type = "Unknown Battery"; -static const char *loading_batt_type = "Loading Battery Data"; -static const char *missing_batt_type = "Disconnected Battery"; - -/* Log buffer */ -struct fg_log_buffer { - size_t rpos; /* Current 'read' position in buffer */ - size_t wpos; /* Current 'write' position in buffer */ - size_t len; /* Length of the buffer */ - char data[0]; /* Log buffer */ -}; - -/* transaction parameters */ -struct fg_trans { - u32 cnt; /* Number of bytes to read */ - u16 addr; /* 12-bit address in SRAM */ - u32 offset; /* Offset of last read data + byte offset */ - struct fg_chip *chip; - struct fg_log_buffer *log; /* log buffer */ - u8 *data; /* fg data that is read */ - struct mutex memif_dfs_lock; /* Prevent thread concurrency */ -}; - -struct fg_dbgfs { - u32 cnt; - u32 addr; - struct fg_chip *chip; - struct dentry *root; - struct mutex lock; - struct debugfs_blob_wrapper help_msg; -}; - -static struct fg_dbgfs dbgfs_data = { - .lock = __MUTEX_INITIALIZER(dbgfs_data.lock), - .help_msg = { - .data = -"FG Debug-FS support\n" -"\n" -"Hierarchy schema:\n" -"/sys/kernel/debug/fg_memif\n" -" /help -- Static help text\n" -" /address -- Starting register address for reads or writes\n" -" /count -- Number of registers to read (only used for reads)\n" -" /data -- Initiates the SRAM read (formatted output)\n" -"\n", - }, -}; - -static const struct of_device_id fg_match_table[] = { - { .compatible = QPNP_FG_DEV_NAME, }, - {} -}; - -static char *fg_supplicants[] = { - "battery", - "bcl", - "fg_adc" -}; - -#define DEBUG_PRINT_BUFFER_SIZE 64 -static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) -{ - int pos = 0; - int i; - - for (i = 0; i < buf_len; i++) { - pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]); - if (i < buf_len - 1) - pos += scnprintf(str + pos, str_len - pos, " "); - } -} - -static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if ((addr & 0xff00) == 0) { - pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_write(chip->regmap, addr, val, len); - if (rc) { - pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return rc; - } - - if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); - pr_info("write(0x%04X), sid=%d, len=%d; %s\n", - addr, to_spmi_device(pdev->dev.parent)->usid, len, - str); - } - - return rc; -} - -static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if ((addr & 0xff00) == 0) { - pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_read(chip->regmap, addr, val, len); - if (rc) { - pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr, - to_spmi_device(pdev->dev.parent)->usid, rc); - return rc; - } - - if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); - pr_info("read(0x%04x), sid=%d, len=%d; %s\n", - addr, to_spmi_device(pdev->dev.parent)->usid, len, - str); - } - - return rc; -} - -static int fg_masked_write(struct fg_chip *chip, u16 addr, - u8 mask, u8 val, int len) -{ - int rc; - - rc = regmap_update_bits(chip->regmap, addr, mask, val); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - return rc; -} - -#define RIF_MEM_ACCESS_REQ BIT(7) -static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status) -{ - int rc; - u8 mem_if_sts; - - rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1); - if (rc) { - pr_err("failed to read rif_mem status rc=%d\n", rc); - return rc; - } - - *status = mem_if_sts & RIF_MEM_ACCESS_REQ; - return 0; -} - -static bool fg_check_sram_access(struct fg_chip *chip) -{ - int rc; - u8 mem_if_sts; - bool rif_mem_sts = false; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return false; - } - - if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) - return false; - - rc = fg_check_rif_mem_access(chip, &rif_mem_sts); - if (rc) - return false; - - return rif_mem_sts; -} - -static inline int fg_assert_sram_access(struct fg_chip *chip) -{ - int rc; - u8 mem_if_sts; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return rc; - } - - if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) { - pr_err("mem_avail not high: %02x\n", mem_if_sts); - return -EINVAL; - } - - rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return rc; - } - - if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) { - pr_err("mem_avail not high: %02x\n", mem_if_sts); - return -EINVAL; - } - - return 0; -} - -#define INTF_CTL_BURST BIT(7) -#define INTF_CTL_WR_EN BIT(6) -static int fg_config_access(struct fg_chip *chip, bool write, - bool burst) -{ - int rc; - u8 intf_ctl = 0; - - intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0); - - rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1); - if (rc) { - pr_err("failed to set mem access bit\n"); - return -EIO; - } - - return rc; -} - -static int fg_req_and_wait_access(struct fg_chip *chip, int timeout) -{ - int rc = 0, ret = 0; - bool tried_again = false; - - if (!fg_check_sram_access(chip)) { - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1); - if (rc) { - pr_err("failed to set mem access bit\n"); - return -EIO; - } - fg_stay_awake(&chip->memif_wakeup_source); - } - -wait: - /* Wait for MEM_AVAIL IRQ. */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_granted, - msecs_to_jiffies(timeout)); - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("transaction timed out rc=%d\n", rc); - return rc; - } - - return rc; -} - -static int fg_release_access(struct fg_chip *chip) -{ - int rc; - - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - RIF_MEM_ACCESS_REQ, 0, 1); - fg_relax(&chip->memif_wakeup_source); - reinit_completion(&chip->sram_access_granted); - - return rc; -} - -static void fg_release_access_if_necessary(struct fg_chip *chip) -{ - mutex_lock(&chip->rw_lock); - if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0) { - fg_release_access(chip); - } - mutex_unlock(&chip->rw_lock); -} - -/* - * fg_mem_lock disallows the fuel gauge to release access until it has been - * released. - * - * an equal number of calls must be made to fg_mem_release for the fuel gauge - * driver to release the sram access. - */ -static void fg_mem_lock(struct fg_chip *chip) -{ - mutex_lock(&chip->rw_lock); - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); -} - -static void fg_mem_release(struct fg_chip *chip) -{ - fg_release_access_if_necessary(chip); -} - -static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) -{ - int rc; - - rc = fg_write(chip, (u8 *) address, - chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", - chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc); - return rc; - } - - return rc; -} - -#define BUF_LEN 4 -static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, - int offset) -{ - int rc, total_len; - u8 *rd_data = val; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - rc = fg_config_access(chip, 0, (len > 4)); - if (rc) - return rc; - - rc = fg_set_ram_addr(chip, &address); - if (rc) - return rc; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("length %d addr=%02X\n", len, address); - - total_len = len; - while (len > 0) { - if (!offset) { - rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip), - min(len, BUF_LEN)); - } else { - rc = fg_read(chip, rd_data, - MEM_INTF_RD_DATA0(chip) + offset, - min(len, BUF_LEN - offset)); - - /* manually set address to allow continous reads */ - address += BUF_LEN; - - rc = fg_set_ram_addr(chip, &address); - if (rc) - return rc; - } - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - MEM_INTF_RD_DATA0(chip) + offset, rc); - return rc; - } - rd_data += (BUF_LEN - offset); - len -= (BUF_LEN - offset); - offset = 0; - } - - if (fg_debug_mask & FG_MEM_DEBUG_READS) { - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); - pr_info("data: %s\n", str); - } - return rc; -} - -static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - int rc = 0, user_cnt = 0, orig_address = address; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - user_cnt = atomic_add_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("user_cnt %d\n", user_cnt); - mutex_lock(&chip->rw_lock); - if (!fg_check_sram_access(chip)) { - rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); - if (rc) - goto out; - } - - rc = fg_sub_mem_read(chip, val, address, len, offset); - -out: - user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("user_cnt %d\n", user_cnt); - - fg_assert_sram_access(chip); - - if (!keep_access && (user_cnt == 0) && !rc) { - rc = fg_release_access(chip); - if (rc) { - pr_err("failed to set mem access bit\n"); - rc = -EIO; - } - } - - mutex_unlock(&chip->rw_lock); - return rc; -} - -static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - int rc = 0, user_cnt = 0, sublen; - bool access_configured = false; - u8 *wr_data = val, word[4]; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if (address < RAM_OFFSET) - return -EINVAL; - - if (offset > 3) - return -EINVAL; - - address = ((address + offset) / 4) * 4; - offset = (address + offset) % 4; - - user_cnt = atomic_add_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("user_cnt %d\n", user_cnt); - mutex_lock(&chip->rw_lock); - if (!fg_check_sram_access(chip)) { - rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); - if (rc) - goto out; - } - - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) { - pr_info("length %d addr=%02X offset=%d\n", - len, address, offset); - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len); - pr_info("writing: %s\n", str); - } - - while (len > 0) { - if (offset != 0) { - sublen = min(4 - offset, len); - rc = fg_sub_mem_read(chip, word, address, 4, 0); - if (rc) - goto out; - memcpy(word + offset, wr_data, sublen); - /* configure access as burst if more to write */ - rc = fg_config_access(chip, 1, (len - sublen) > 0); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - offset = 0; - access_configured = true; - } else if (len >= 4) { - if (!access_configured) { - rc = fg_config_access(chip, 1, len > 4); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - access_configured = true; - } - sublen = 4; - memcpy(word, wr_data, 4); - } else if (len > 0 && len < 4) { - sublen = len; - rc = fg_sub_mem_read(chip, word, address, 4, 0); - if (rc) - goto out; - memcpy(word, wr_data, sublen); - rc = fg_config_access(chip, 1, 0); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - access_configured = true; - } else { - pr_err("Invalid length: %d\n", len); - break; - } - rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4); - if (rc) { - pr_err("spmi write failed: addr=%03x, rc=%d\n", - MEM_INTF_WR_DATA0(chip), rc); - goto out; - } - len -= sublen; - wr_data += sublen; - address += 4; - } - -out: - user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("user_cnt %d\n", user_cnt); - - fg_assert_sram_access(chip); - - if (!keep_access && (user_cnt == 0) && !rc) { - rc = fg_release_access(chip); - if (rc) { - pr_err("failed to set mem access bit\n"); - rc = -EIO; - } - } - - mutex_unlock(&chip->rw_lock); - return rc; -} - -#define MEM_INTF_IMA_CFG 0x52 -#define MEM_INTF_IMA_OPR_STS 0x54 -#define MEM_INTF_IMA_ERR_STS 0x5F -#define MEM_INTF_IMA_EXP_STS 0x55 -#define MEM_INTF_IMA_HW_STS 0x56 -#define MEM_INTF_IMA_BYTE_EN 0x60 -#define IMA_ADDR_STBL_ERR BIT(7) -#define IMA_WR_ACS_ERR BIT(6) -#define IMA_RD_ACS_ERR BIT(5) -#define IMA_IACS_CLR BIT(2) -#define IMA_IACS_RDY BIT(1) -static int fg_check_ima_exception(struct fg_chip *chip) -{ - int rc = 0, ret = 0; - u8 err_sts, exp_sts = 0, hw_sts = 0; - - rc = fg_read(chip, &err_sts, - chip->mem_base + MEM_INTF_IMA_ERR_STS, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - return rc; - } - - if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) { - u8 temp; - - fg_read(chip, &exp_sts, - chip->mem_base + MEM_INTF_IMA_EXP_STS, 1); - fg_read(chip, &hw_sts, - chip->mem_base + MEM_INTF_IMA_HW_STS, 1); - pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", - err_sts, exp_sts, hw_sts); - rc = err_sts; - - /* clear the error */ - ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG, - IMA_IACS_CLR, IMA_IACS_CLR, 1); - temp = 0x4; - ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1); - temp = 0x0; - ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1); - ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1); - ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG, - IMA_IACS_CLR, 0, 1); - if (!ret) - return -EAGAIN; - else - pr_err("Error clearing IMA exception ret=%d\n", ret); - } - - return rc; -} - -static int fg_check_iacs_ready(struct fg_chip *chip) -{ - int rc = 0, timeout = 250; - u8 ima_opr_sts = 0; - - /* - * Additional delay to make sure IACS ready bit is set after - * Read/Write operation. - */ - - usleep_range(30, 35); - while (1) { - rc = fg_read(chip, &ima_opr_sts, - chip->mem_base + MEM_INTF_IMA_OPR_STS, 1); - if (!rc && (ima_opr_sts & IMA_IACS_RDY)) { - break; - } else { - if (!(--timeout) || rc) - break; - /* delay for iacs_ready to be asserted */ - usleep_range(5000, 7000); - } - } - - if (!timeout || rc) { - pr_err("IACS_RDY not set\n"); - /* perform IACS_CLR sequence */ - fg_check_ima_exception(chip); - return -EBUSY; - } - - return 0; -} - -#define IACS_SLCT BIT(5) -static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, - u16 address, int offset, int len) -{ - int rc = 0, i; - u8 *word = val, byte_enable = 0, num_bytes = 0; - - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("length %d addr=%02X offset=%d\n", - len, address, offset); - - while (len > 0) { - num_bytes = (offset + len) > BUF_LEN ? - (BUF_LEN - offset) : len; - /* write to byte_enable */ - for (i = offset; i < (offset + num_bytes); i++) - byte_enable |= BIT(i); - - rc = fg_write(chip, &byte_enable, - chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1); - if (rc) { - pr_err("Unable to write to byte_en_reg rc=%d\n", - rc); - return rc; - } - /* write data */ - rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset, - num_bytes); - if (rc) { - pr_err("spmi write failed: addr=%03x, rc=%d\n", - MEM_INTF_WR_DATA0(chip) + offset, rc); - return rc; - } - /* - * The last-byte WR_DATA3 starts the write transaction. - * Write a dummy value to WR_DATA3 if it does not have - * valid data. This dummy data is not written to the - * SRAM as byte_en for WR_DATA3 is not set. - */ - if (!(byte_enable & BIT(3))) { - u8 dummy_byte = 0x0; - rc = fg_write(chip, &dummy_byte, - MEM_INTF_WR_DATA0(chip) + 3, 1); - if (rc) { - pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n", - rc); - return rc; - } - } - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* check for error condition */ - rc = fg_check_ima_exception(chip); - if (rc) { - pr_err("IMA transaction failed rc=%d", rc); - return rc; - } - - word += num_bytes; - len -= num_bytes; - offset = byte_enable = 0; - } - - return rc; -} - -static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int offset, int len) -{ - int rc = 0, total_len; - u8 *rd_data = val, num_bytes; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("length %d addr=%02X\n", len, address); - - total_len = len; - while (len > 0) { - num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len; - rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset, - num_bytes); - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - MEM_INTF_RD_DATA0(chip) + offset, rc); - return rc; - } - - rd_data += num_bytes; - len -= num_bytes; - offset = 0; - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* check for error condition */ - rc = fg_check_ima_exception(chip); - if (rc) { - pr_err("IMA transaction failed rc=%d", rc); - return rc; - } - - if (len && (len + offset) < BUF_LEN) { - /* move to single mode */ - u8 intr_ctl = 0; - - rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1); - if (rc) { - pr_err("failed to move to single mode rc=%d\n", - rc); - return -EIO; - } - } - } - - if (fg_debug_mask & FG_MEM_DEBUG_READS) { - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); - pr_info("data: %s\n", str); - } - - return rc; -} - -#define IMA_REQ_ACCESS (IACS_SLCT | RIF_MEM_ACCESS_REQ) -static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, - u16 address, int len, int offset, int op) -{ - int rc = 0; - bool rif_mem_sts = true; - int time_count = 0; - - while (1) { - rc = fg_check_rif_mem_access(chip, &rif_mem_sts); - if (rc) - return rc; - - if (!rif_mem_sts) - break; - - if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n", - op ? "write" : "read"); - - /* - * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not - * clear, then return an error instead of waiting for it again. - */ - if (time_count > 4) { - pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n"); - return -ETIMEDOUT; - } - - /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */ - usleep_range(4000, 4100); - time_count++; - } - - /* configure for IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1); - if (rc) { - pr_err("failed to set mem access bit rc = %d\n", rc); - return rc; - } - - /* configure for the read/write single/burst mode */ - rc = fg_config_access(chip, op, (offset + len) > 4); - if (rc) { - pr_err("failed to set configure memory access rc = %d\n", rc); - return rc; - } - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* write addresses to the register */ - rc = fg_set_ram_addr(chip, &address); - if (rc) { - pr_err("failed to set SRAM address rc = %d\n", rc); - return rc; - } - - rc = fg_check_iacs_ready(chip); - if (rc) - pr_debug("IACS_RDY failed rc=%d\n", rc); - - return rc; -} - -#define MEM_INTF_FG_BEAT_COUNT 0x57 -#define BEAT_COUNT_MASK 0x0F -#define RETRY_COUNT 3 -static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset) -{ - int rc = 0, orig_address = address; - u8 start_beat_count, end_beat_count, count = 0; - bool retry = false; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - fg_stay_awake(&chip->memif_wakeup_source); - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - if (address < RAM_OFFSET) { - /* - * OTP memory reads need a conventional memory access, do a - * conventional read when SRAM offset < RAM_OFFSET. - */ - rc = fg_conventional_mem_read(chip, val, address, len, offset, - 0); - if (rc) - pr_err("Failed to read OTP memory %d\n", rc); - goto exit; - } - - mutex_lock(&chip->rw_lock); - -retry: - rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0); - if (rc) { - pr_err("failed to configure SRAM for IMA rc = %d\n", rc); - goto out; - } - - /* read the start beat count */ - rc = fg_read(chip, &start_beat_count, - chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - goto out; - } - - /* read data */ - rc = __fg_interleaved_mem_read(chip, val, address, offset, len); - if (rc) { - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - count++; - pr_err("IMA access failed retry_count = %d\n", count); - goto retry; - } else { - pr_err("failed to read SRAM address rc = %d\n", rc); - goto out; - } - } - - /* read the end beat count */ - rc = fg_read(chip, &end_beat_count, - chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - goto out; - } - - start_beat_count &= BEAT_COUNT_MASK; - end_beat_count &= BEAT_COUNT_MASK; - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("Start beat_count = %x End beat_count = %x\n", - start_beat_count, end_beat_count); - if (start_beat_count != end_beat_count) { - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("Beat count do not match - retry transaction\n"); - retry = true; - } -out: - /* Release IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1); - if (rc) - pr_err("failed to reset IMA access bit rc = %d\n", rc); - - if (retry) { - retry = false; - goto retry; - } - mutex_unlock(&chip->rw_lock); - -exit: - fg_relax(&chip->memif_wakeup_source); - return rc; -} - -static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset) -{ - int rc = 0, orig_address = address; - u8 count = 0; - - if (address < RAM_OFFSET) - return -EINVAL; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - fg_stay_awake(&chip->memif_wakeup_source); - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - mutex_lock(&chip->rw_lock); - -retry: - rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1); - if (rc) { - pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc); - goto out; - } - - /* write data */ - rc = __fg_interleaved_mem_write(chip, val, address, offset, len); - if (rc) { - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - count++; - pr_err("IMA access failed retry_count = %d\n", count); - goto retry; - } else { - pr_err("failed to write SRAM address rc = %d\n", rc); - goto out; - } - } - -out: - /* Release IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1); - if (rc) - pr_err("failed to reset IMA access bit rc = %d\n", rc); - - mutex_unlock(&chip->rw_lock); - fg_relax(&chip->memif_wakeup_source); - return rc; -} - -static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - if (chip->ima_supported) - return fg_interleaved_mem_read(chip, val, address, - len, offset); - else - return fg_conventional_mem_read(chip, val, address, - len, offset, keep_access); -} - -static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - if (chip->ima_supported) - return fg_interleaved_mem_write(chip, val, address, - len, offset); - else - return fg_conventional_mem_write(chip, val, address, - len, offset, keep_access); -} - -static int fg_mem_masked_write(struct fg_chip *chip, u16 addr, - u8 mask, u8 val, u8 offset) -{ - int rc = 0; - u8 reg[4]; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - rc = fg_mem_read(chip, reg, addr, 4, 0, 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - reg[offset] &= ~mask; - reg[offset] |= val & mask; - - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4); - pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset); - - rc = fg_mem_write(chip, reg, addr, 4, 0, 0); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - return rc; -} - -static int soc_to_setpoint(int soc) -{ - return DIV_ROUND_CLOSEST(soc * 255, 100); -} - -static void batt_to_setpoint_adc(int vbatt_mv, u8 *data) -{ - int val; - /* Battery voltage is an offset from 0 V and LSB is 1/2^15. */ - val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000); - data[0] = val & 0xFF; - data[1] = val >> 8; - return; -} - -static u8 batt_to_setpoint_8b(int vbatt_mv) -{ - int val; - /* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */ - val = (vbatt_mv - 2500) * 512 / 1000; - return DIV_ROUND_CLOSEST(val, 5); -} - -static u8 therm_delay_to_setpoint(u32 delay_us) -{ - u8 val; - - if (delay_us < 2560) - val = 0; - else if (delay_us > 163840) - val = 7; - else - val = ilog2(delay_us / 10) - 7; - return val << 5; -} - -static int get_current_time(unsigned long *now_tm_sec) -{ - struct rtc_time tm; - struct rtc_device *rtc; - int rc; - - rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); - if (rtc == NULL) { - pr_err("%s: unable to open rtc device (%s)\n", - __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); - return -EINVAL; - } - - rc = rtc_read_time(rtc, &tm); - if (rc) { - pr_err("Error reading rtc device (%s) : %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - - rc = rtc_valid_tm(&tm); - if (rc) { - pr_err("Invalid RTC time (%s): %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - rtc_tm_to_time(&tm, now_tm_sec); - -close_time: - rtc_class_close(rtc); - return rc; -} - -#define BATTERY_SOC_REG 0x56C -#define BATTERY_SOC_OFFSET 1 -#define FULL_PERCENT_3B 0xFFFFFF -static int get_battery_soc_raw(struct fg_chip *chip) -{ - int rc; - u8 buffer[3]; - - rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0); - if (rc) { - pr_err("Unable to read battery soc: %d\n", rc); - return 0; - } - return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]); -} - -#define COUNTER_IMPTR_REG 0X558 -#define COUNTER_PULSE_REG 0X55C -#define SOC_FULL_REG 0x564 -#define COUNTER_IMPTR_OFFSET 2 -#define COUNTER_PULSE_OFFSET 0 -#define SOC_FULL_OFFSET 3 -#define ESR_PULSE_RECONFIG_SOC 0xFFF971 -static int fg_configure_soc(struct fg_chip *chip) -{ - u32 batt_soc; - u8 cntr[2] = {0, 0}; - int rc = 0; - - mutex_lock(&chip->rw_lock); - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); - - /* Read Battery SOC */ - batt_soc = get_battery_soc_raw(chip); - - if (batt_soc > ESR_PULSE_RECONFIG_SOC) { - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("Configuring soc registers batt_soc: %x\n", - batt_soc); - batt_soc = ESR_PULSE_RECONFIG_SOC; - rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3, - BATTERY_SOC_OFFSET, 1); - if (rc) { - pr_err("failed to write BATT_SOC rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3, - SOC_FULL_OFFSET, 1); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2, - COUNTER_IMPTR_OFFSET, 1); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2, - COUNTER_PULSE_OFFSET, 0); - if (rc) - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - } -out: - fg_release_access_if_necessary(chip); - return rc; -} - -#define SOC_EMPTY BIT(3) -static bool fg_is_batt_empty(struct fg_chip *chip) -{ - u8 fg_soc_sts; - int rc; - - rc = fg_read(chip, &fg_soc_sts, - INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - return false; - } - - return (fg_soc_sts & SOC_EMPTY) != 0; -} - -static int get_monotonic_soc_raw(struct fg_chip *chip) -{ - u8 cap[2]; - int rc, tries = 0; - - while (tries < MAX_TRIES_SOC) { - rc = fg_read(chip, cap, - chip->soc_base + SOC_MONOTONIC_SOC, 2); - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - chip->soc_base + SOC_MONOTONIC_SOC, rc); - return rc; - } - - if (cap[0] == cap[1]) - break; - - tries++; - } - - if (tries == MAX_TRIES_SOC) { - pr_err("shadow registers do not match\n"); - return -EINVAL; - } - - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info_ratelimited("raw: 0x%02x\n", cap[0]); - return cap[0]; -} - -#define EMPTY_CAPACITY 0 -#define DEFAULT_CAPACITY 50 -#define MISSING_CAPACITY 100 -#define FULL_CAPACITY 100 -#define FULL_SOC_RAW 0xFF -static int get_prop_capacity(struct fg_chip *chip) -{ - int msoc; - - if (chip->battery_missing) - return MISSING_CAPACITY; - if (!chip->profile_loaded && !chip->use_otp_profile) - return DEFAULT_CAPACITY; - if (chip->charge_full) - return FULL_CAPACITY; - if (chip->soc_empty) { - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info_ratelimited("capacity: %d, EMPTY\n", - EMPTY_CAPACITY); - return EMPTY_CAPACITY; - } - msoc = get_monotonic_soc_raw(chip); - if (msoc == 0) - return EMPTY_CAPACITY; - else if (msoc == FULL_SOC_RAW) - return FULL_CAPACITY; - return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2), - FULL_SOC_RAW - 2) + 1; -} - -#define HIGH_BIAS 3 -#define MED_BIAS BIT(1) -#define LOW_BIAS BIT(0) -static u8 bias_ua[] = { - [HIGH_BIAS] = 150, - [MED_BIAS] = 15, - [LOW_BIAS] = 5, -}; - -static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info) -{ - u64 battery_id_ohm; - - if ((bid_info & 0x3) == 0) { - pr_err("can't determine battery id 0x%02x\n", bid_info); - return -EINVAL; - } - - battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]); - - return battery_id_ohm; -} - -#define DEFAULT_TEMP_DEGC 250 -static int get_sram_prop_now(struct fg_chip *chip, unsigned int type) -{ - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d value %d\n", - fg_data[type].address, fg_data[type].offset, - fg_data[type].value); - - if (type == FG_DATA_BATT_ID) - return get_batt_id(fg_data[type].value, - fg_data[FG_DATA_BATT_ID_INFO].value); - - return fg_data[type].value; -} - -#define MIN_TEMP_DEGC -300 -#define MAX_TEMP_DEGC 970 -static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) -{ - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d\n", settings[type].address, - settings[type].offset); - - return settings[type].value; -} - -static int set_prop_jeita_temp(struct fg_chip *chip, - unsigned int type, int decidegc) -{ - int rc = 0; - - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d temp%d\n", - settings[type].address, - settings[type].offset, decidegc); - - settings[type].value = decidegc; - - cancel_delayed_work_sync( - &chip->update_jeita_setting); - schedule_delayed_work( - &chip->update_jeita_setting, 0); - - return rc; -} - -#define EXTERNAL_SENSE_SELECT 0x4AC -#define EXTERNAL_SENSE_OFFSET 0x2 -#define EXTERNAL_SENSE_BIT BIT(2) -static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type) -{ - int rc; - - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - EXTERNAL_SENSE_BIT, - ext_sense_type ? EXTERNAL_SENSE_BIT : 0, - EXTERNAL_SENSE_OFFSET); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - return rc; - } - - return 0; -} - -#define EXPONENT_MASK 0xF800 -#define MANTISSA_MASK 0x3FF -#define SIGN BIT(10) -#define EXPONENT_SHIFT 11 -#define MICRO_UNIT 1000000ULL -static int64_t float_decode(u16 reg) -{ - int64_t final_val, exponent_val, mantissa_val; - int exponent, mantissa, n; - bool sign; - - exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT; - mantissa = (reg & MANTISSA_MASK); - sign = !!(reg & SIGN); - - pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign); - - mantissa_val = mantissa * MICRO_UNIT; - - n = exponent - 15; - if (n < 0) - exponent_val = MICRO_UNIT >> -n; - else - exponent_val = MICRO_UNIT << n; - - n = n - 10; - if (n < 0) - mantissa_val >>= -n; - else - mantissa_val <<= n; - - final_val = exponent_val + mantissa_val; - - if (sign) - final_val *= -1; - - return final_val; -} - -#define MIN_HALFFLOAT_EXP_N -15 -#define MAX_HALFFLOAT_EXP_N 16 -static int log2_floor(int64_t uval) -{ - int n = 0; - int64_t i = MICRO_UNIT; - - if (uval > i) { - while (uval > i && n > MIN_HALFFLOAT_EXP_N) { - i <<= 1; - n += 1; - } - if (uval < i) - n -= 1; - } else if (uval < i) { - while (uval < i && n < MAX_HALFFLOAT_EXP_N) { - i >>= 1; - n -= 1; - } - } - - return n; -} - -static int64_t exp2_int(int64_t n) -{ - int p = n - 1; - - if (p > 0) - return (2 * MICRO_UNIT) << p; - else - return (2 * MICRO_UNIT) >> abs(p); -} - -static u16 float_encode(int64_t uval) -{ - int sign = 0, n, exp, mantissa; - u16 half = 0; - - if (uval < 0) { - sign = 1; - uval = abs(uval); - } - n = log2_floor(uval); - exp = n + 15; - mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n), - MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT); - - half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN) - | ((exp << 11) & EXPONENT_MASK); - - if (fg_debug_mask & FG_STATUS) - pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n", - uval, mantissa, sign, exp, half); - return half; -} - -#define BATT_IDED BIT(3) -static int fg_is_batt_id_valid(struct fg_chip *chip) -{ - u8 fg_batt_sts; - int rc; - - rc = fg_read(chip, &fg_batt_sts, - INT_RT_STS(chip->batt_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - return rc; - } - - if (fg_debug_mask & FG_IRQS) - pr_info("fg batt sts 0x%x\n", fg_batt_sts); - - return (fg_batt_sts & BATT_IDED) ? 1 : 0; -} - -static int64_t twos_compliment_extend(int64_t val, int nbytes) -{ - int i; - int64_t mask; - - mask = 0x80LL << ((nbytes - 1) * 8); - if (val & mask) { - for (i = 8; i > nbytes; i--) { - mask = 0xFFLL << ((i - 1) * 8); - val |= mask; - } - } - - return val; -} - -#define LSB_24B_NUMRTR 596046 -#define LSB_24B_DENMTR 1000000 -#define LSB_16B_NUMRTR 152587 -#define LSB_16B_DENMTR 1000 -#define LSB_8B 9800 -#define TEMP_LSB_16B 625 -#define DECIKELVIN 2730 -#define SRAM_PERIOD_NO_ID_UPDATE_MS 100 -#define FULL_PERCENT_28BIT 0xFFFFFFF -static void update_sram_data(struct fg_chip *chip, int *resched_ms) -{ - int i, j, rc = 0; - u8 reg[4]; - int64_t temp; - int battid_valid = fg_is_batt_id_valid(chip); - - fg_stay_awake(&chip->update_sram_wakeup_source); - if (chip->fg_restarting) - goto resched; - - fg_mem_lock(chip); - for (i = 1; i < FG_DATA_MAX; i++) { - if (chip->profile_loaded && i >= FG_DATA_BATT_ID) - continue; - rc = fg_mem_read(chip, reg, fg_data[i].address, - fg_data[i].len, fg_data[i].offset, 0); - if (rc) { - pr_err("Failed to update sram data\n"); - break; - } - - temp = 0; - for (j = 0; j < fg_data[i].len; j++) - temp |= reg[j] << (8 * j); - - switch (i) { - case FG_DATA_OCV: - case FG_DATA_VOLTAGE: - case FG_DATA_CPRED_VOLTAGE: - fg_data[i].value = div_u64( - (u64)(u16)temp * LSB_16B_NUMRTR, - LSB_16B_DENMTR); - break; - case FG_DATA_CURRENT: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div_s64( - (s64)temp * LSB_16B_NUMRTR, - LSB_16B_DENMTR); - break; - case FG_DATA_BATT_ESR: - fg_data[i].value = float_decode((u16) temp); - break; - case FG_DATA_BATT_ESR_COUNT: - fg_data[i].value = (u16)temp; - break; - case FG_DATA_BATT_ID: - if (battid_valid) - fg_data[i].value = reg[0] * LSB_8B; - break; - case FG_DATA_BATT_ID_INFO: - if (battid_valid) - fg_data[i].value = reg[0]; - break; - case FG_DATA_BATT_SOC: - fg_data[i].value = div64_s64((temp * 10000), - FULL_PERCENT_3B); - break; - case FG_DATA_CC_CHARGE: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div64_s64( - temp * (int64_t)chip->nom_cap_uah, - FULL_PERCENT_28BIT); - break; - case FG_DATA_VINT_ERR: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div64_s64(temp * chip->nom_cap_uah, - FULL_PERCENT_3B); - break; - }; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("%d %lld %d\n", i, temp, fg_data[i].value); - } - fg_mem_release(chip); - - if (!rc) - get_current_time(&chip->last_sram_update_time); - -resched: - if (battid_valid) { - complete_all(&chip->batt_id_avail); - *resched_ms = fg_sram_update_period_ms; - } else { - *resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS; - } - fg_relax(&chip->update_sram_wakeup_source); -} - -#define SRAM_TIMEOUT_MS 3000 -static void update_sram_data_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_sram_data.work); - int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret; - bool tried_again = false; - -wait: - /* Wait for MEMIF access revoked */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_revoked, - msecs_to_jiffies(SRAM_TIMEOUT_MS)); - - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - pr_err("transaction timed out ret=%d\n", ret); - goto out; - } - update_sram_data(chip, &resched_ms); - -out: - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(resched_ms)); -} - -#define BATT_TEMP_OFFSET 3 -#define BATT_TEMP_CNTRL_MASK 0x17 -#define DISABLE_THERM_BIT BIT(0) -#define TEMP_SENSE_ALWAYS_BIT BIT(1) -#define TEMP_SENSE_CHARGE_BIT BIT(2) -#define FORCE_RBIAS_ON_BIT BIT(4) -#define BATT_TEMP_OFF DISABLE_THERM_BIT -#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \ - TEMP_SENSE_CHARGE_BIT) -#define TEMP_PERIOD_UPDATE_MS 10000 -#define TEMP_PERIOD_TIMEOUT_MS 3000 -static void update_temp_data(struct work_struct *work) -{ - s16 temp; - u8 reg[2]; - bool tried_again = false; - int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS; - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_temp_work.work); - - if (chip->fg_restarting) - goto resched; - - fg_stay_awake(&chip->update_temp_wakeup_source); - if (chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - BATT_TEMP_ON, - BATT_TEMP_OFFSET); - if (rc) { - pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc); - goto out; - } - -wait: - /* Wait for MEMIF access revoked */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_revoked, - msecs_to_jiffies(timeout)); - - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("transaction timed out ret=%d\n", ret); - goto out; - } - } - - /* Read FG_DATA_BATT_TEMP now */ - rc = fg_mem_read(chip, reg, fg_data[0].address, - fg_data[0].len, fg_data[0].offset, - chip->sw_rbias_ctrl ? 1 : 0); - if (rc) { - pr_err("Failed to update temp data\n"); - goto out; - } - - temp = reg[0] | (reg[1] << 8); - fg_data[0].value = (temp * TEMP_LSB_16B / 1000) - - DECIKELVIN; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value); - - get_current_time(&chip->last_temp_update_time); - -out: - if (chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - BATT_TEMP_OFF, - BATT_TEMP_OFFSET); - if (rc) - pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc); - } - fg_relax(&chip->update_temp_wakeup_source); - -resched: - schedule_delayed_work( - &chip->update_temp_work, - msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS)); -} - -static void update_jeita_setting(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_jeita_setting.work); - u8 reg[4]; - int i, rc; - - for (i = 0; i < 4; i++) - reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30; - - rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address, - 4, settings[FG_MEM_SOFT_COLD].offset, 0); - if (rc) - pr_err("failed to update JEITA setting rc=%d\n", rc); -} - -static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold) -{ - u16 address; - int offset, rc; - - address = settings[FG_MEM_RESUME_SOC].address; - offset = settings[FG_MEM_RESUME_SOC].offset; - - rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset); - - if (rc) - pr_err("write failed rc=%d\n", rc); - else - pr_debug("setting resume-soc to %x\n", threshold); - - return rc; -} - -#define VBATT_LOW_STS_BIT BIT(2) -static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts) -{ - int rc = 0; - u8 fg_batt_sts; - - rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1); - if (!rc) - *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT); - return rc; -} - -#define BATT_CYCLE_NUMBER_REG 0x5E8 -#define BATT_CYCLE_OFFSET 0 -static void restore_cycle_counter(struct fg_chip *chip) -{ - int rc = 0, i, address; - u8 data[2]; - - fg_mem_lock(chip); - for (i = 0; i < BUCKET_COUNT; i++) { - address = BATT_CYCLE_NUMBER_REG + i * 2; - rc = fg_mem_read(chip, (u8 *)&data, address, 2, - BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n", - i, rc); - else - chip->cyc_ctr.count[i] = data[0] | data[1] << 8; - } - fg_mem_release(chip); -} - -static void clear_cycle_counter(struct fg_chip *chip) -{ - int rc = 0, len, i; - - if (!chip->cyc_ctr.en) - return; - - len = sizeof(chip->cyc_ctr.count); - memset(chip->cyc_ctr.count, 0, len); - for (i = 0; i < BUCKET_COUNT; i++) { - chip->cyc_ctr.started[i] = false; - chip->cyc_ctr.last_soc[i] = 0; - } - rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count, - BATT_CYCLE_NUMBER_REG, len, - BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc); -} - -static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) -{ - int rc = 0, address; - u16 cyc_count; - u8 data[2]; - - if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) - return 0; - - cyc_count = chip->cyc_ctr.count[bucket]; - cyc_count++; - data[0] = cyc_count & 0xFF; - data[1] = cyc_count >> 8; - - address = BATT_CYCLE_NUMBER_REG + bucket * 2; - - rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n", - bucket, rc); - else - chip->cyc_ctr.count[bucket] = cyc_count; - return rc; -} - -static void update_cycle_count(struct work_struct *work) -{ - int rc = 0, bucket, i; - u8 reg[3], batt_soc; - struct fg_chip *chip = container_of(work, - struct fg_chip, - cycle_count_work); - - mutex_lock(&chip->cyc_ctr.lock); - rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, - BATTERY_SOC_OFFSET, 0); - if (rc) { - pr_err("Failed to read battery soc rc: %d\n", rc); - goto out; - } - batt_soc = reg[2]; - - if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { - /* Find out which bucket the SOC falls in */ - bucket = batt_soc / BUCKET_SOC_PCT; - - if (fg_debug_mask & FG_STATUS) - pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket); - - /* - * If we've started counting for the previous bucket, - * then store the counter for that bucket if the - * counter for current bucket is getting started. - */ - if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && - !chip->cyc_ctr.started[bucket]) { - rc = fg_inc_store_cycle_ctr(chip, bucket - 1); - if (rc) { - pr_err("Error in storing cycle_ctr rc: %d\n", - rc); - goto out; - } else { - chip->cyc_ctr.started[bucket - 1] = false; - chip->cyc_ctr.last_soc[bucket - 1] = 0; - } - } - if (!chip->cyc_ctr.started[bucket]) { - chip->cyc_ctr.started[bucket] = true; - chip->cyc_ctr.last_soc[bucket] = batt_soc; - } - } else { - for (i = 0; i < BUCKET_COUNT; i++) { - if (chip->cyc_ctr.started[i] && - batt_soc > chip->cyc_ctr.last_soc[i]) { - rc = fg_inc_store_cycle_ctr(chip, i); - if (rc) - pr_err("Error in storing cycle_ctr rc: %d\n", - rc); - chip->cyc_ctr.last_soc[i] = 0; - } - chip->cyc_ctr.started[i] = false; - } - } -out: - mutex_unlock(&chip->cyc_ctr.lock); -} - -static int fg_get_cycle_count(struct fg_chip *chip) -{ - int count; - - if (!chip->cyc_ctr.en) - return 0; - - if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT)) - return -EINVAL; - - mutex_lock(&chip->cyc_ctr.lock); - count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; - mutex_unlock(&chip->cyc_ctr.lock); - return count; -} - -static void half_float_to_buffer(int64_t uval, u8 *buffer) -{ - u16 raw; - - raw = float_encode(uval); - buffer[0] = (u8)(raw & 0xFF); - buffer[1] = (u8)((raw >> 8) & 0xFF); -} - -static int64_t half_float(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - return float_decode(val); -} - -static int voltage_2b(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - /* the range of voltage 2b is [-5V, 5V], so it will fit in an int */ - return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR); -} - -static int bcap_uah_2b(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - return ((int)val) * 1000; -} - -static int lookup_ocv_for_soc(struct fg_chip *chip, int soc) -{ - int64_t *coeffs; - - if (soc > chip->ocv_junction_p1p2 * 10) - coeffs = chip->ocv_coeffs; - else if (soc > chip->ocv_junction_p2p3 * 10) - coeffs = chip->ocv_coeffs + 4; - else - coeffs = chip->ocv_coeffs + 8; - /* the range of ocv will fit in a 32 bit int */ - return (int)(coeffs[0] - + div_s64(coeffs[1] * soc, 1000LL) - + div_s64(coeffs[2] * soc * soc, 1000000LL) - + div_s64(coeffs[3] * soc * soc * soc, 1000000000LL)); -} - -static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv) -{ - int64_t val; - int soc = -EINVAL; - /* - * binary search variables representing the valid start and end - * percentages to search - */ - int start = 0, end = 1000, mid; - - if (fg_debug_mask & FG_AGING) - pr_info("target_ocv = %d\n", ocv); - /* do a binary search for the closest soc to match the ocv */ - while (end - start > 1) { - mid = (start + end) / 2; - val = lookup_ocv_for_soc(chip, mid); - if (fg_debug_mask & FG_AGING) - pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n", - start, mid, end, val); - if (ocv < val) { - end = mid; - } else if (ocv > val) { - start = mid; - } else { - soc = mid; - break; - } - } - /* - * if the exact soc was not found and there are two or less values - * remaining, just compare them and see which one is closest to the ocv - */ - if (soc == -EINVAL) { - if (abs(ocv - lookup_ocv_for_soc(chip, start)) - > abs(ocv - lookup_ocv_for_soc(chip, end))) - soc = end; - else - soc = start; - } - if (fg_debug_mask & FG_AGING) - pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n", - soc, ocv, lookup_ocv_for_soc(chip, soc)); - return soc; -} - -#define ESR_ACTUAL_REG 0x554 -#define BATTERY_ESR_REG 0x4F4 -#define TEMP_RS_TO_RSLOW_REG 0x514 -static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity) -{ - int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow; - int64_t esr_actual, battery_esr, val; - int soc_cutoff_aged, soc_cutoff_new, rc; - int battery_soc, unusable_soc, batt_temp; - u8 buffer[3]; - - if (chip->batt_aging_mode != FG_AGING_ESR) - return 0; - - if (chip->nom_cap_uah == 0) { - if (fg_debug_mask & FG_AGING) - pr_info("ocv coefficients not loaded, aborting\n"); - return 0; - } - fg_mem_lock(chip); - - batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - if (batt_temp < 150 || batt_temp > 400) { - if (fg_debug_mask & FG_AGING) - pr_info("Battery temp (%d) out of range, aborting\n", - (int)batt_temp); - rc = 0; - goto done; - } - - battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B; - if (battery_soc < 25 || battery_soc > 75) { - if (fg_debug_mask & FG_AGING) - pr_info("Battery SoC (%d) out of range, aborting\n", - (int)battery_soc); - rc = 0; - goto done; - } - - rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0); - esr_actual = half_float(buffer); - rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0); - battery_esr = half_float(buffer); - - if (rc) { - goto error_done; - } else if (esr_actual < battery_esr) { - if (fg_debug_mask & FG_AGING) - pr_info("Batt ESR lower than ESR actual, aborting\n"); - rc = 0; - goto done; - } - rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0); - temp_rs_to_rslow = half_float(buffer); - - if (rc) - goto error_done; - - fg_mem_release(chip); - - if (fg_debug_mask & FG_AGING) { - pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n", - battery_soc, chip->cutoff_voltage, - chip->evaluation_current); - pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n", - temp_rs_to_rslow, battery_esr, esr_actual); - } - - /* calculate soc_cutoff_new */ - val = (1000000LL + temp_rs_to_rslow) * battery_esr; - do_div(val, 1000000); - ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000) - + chip->cutoff_voltage; - - /* calculate soc_cutoff_aged */ - val = (1000000LL + temp_rs_to_rslow) * esr_actual; - do_div(val, 1000000); - ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000) - + chip->cutoff_voltage; - - if (fg_debug_mask & FG_AGING) - pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n", - ocv_cutoff_new, ocv_cutoff_aged); - - soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new); - soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged); - - if (fg_debug_mask & FG_AGING) - pr_info("aged soc = %d, new soc = %d\n", - soc_cutoff_aged, soc_cutoff_new); - unusable_soc = soc_cutoff_aged - soc_cutoff_new; - - *actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah) - * (1000 - unusable_soc), 1000); - if (fg_debug_mask & FG_AGING) - pr_info("nom cap = %d, actual cap = %d\n", - chip->nom_cap_uah, *actual_capacity); - - return rc; - -error_done: - pr_err("some register reads failed: %d\n", rc); -done: - fg_mem_release(chip); - return rc; -} - -static void battery_age_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - battery_age_work); - - estimate_battery_age(chip, &chip->actual_cap_uah); -} - -static enum power_supply_property fg_power_props[] = { - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_RAW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_OCV, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_NOW_RAW, - POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_COOL_TEMP, - POWER_SUPPLY_PROP_WARM_TEMP, - POWER_SUPPLY_PROP_RESISTANCE, - POWER_SUPPLY_PROP_RESISTANCE_ID, - POWER_SUPPLY_PROP_BATTERY_TYPE, - POWER_SUPPLY_PROP_UPDATE_NOW, - POWER_SUPPLY_PROP_ESR_COUNT, - POWER_SUPPLY_PROP_VOLTAGE_MIN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_CYCLE_COUNT_ID, - POWER_SUPPLY_PROP_HI_POWER, -}; - -static int fg_power_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - bool vbatt_low_sts; - - switch (psp) { - case POWER_SUPPLY_PROP_BATTERY_TYPE: - if (chip->battery_missing) - val->strval = missing_batt_type; - else if (chip->fg_restarting) - val->strval = loading_batt_type; - else - val->strval = chip->batt_type; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_prop_capacity(chip); - break; - case POWER_SUPPLY_PROP_CAPACITY_RAW: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC); - break; - case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR: - val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT); - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE); - break; - case POWER_SUPPLY_PROP_VOLTAGE_OCV: - val->intval = get_sram_prop_now(chip, FG_DATA_OCV); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = chip->batt_max_voltage_uv; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - break; - case POWER_SUPPLY_PROP_COOL_TEMP: - val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD); - break; - case POWER_SUPPLY_PROP_WARM_TEMP: - val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT); - break; - case POWER_SUPPLY_PROP_RESISTANCE: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR); - break; - case POWER_SUPPLY_PROP_ESR_COUNT: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT); - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT: - val->intval = fg_get_cycle_count(chip); - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - val->intval = chip->cyc_ctr.id; - break; - case POWER_SUPPLY_PROP_RESISTANCE_ID: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID); - break; - case POWER_SUPPLY_PROP_UPDATE_NOW: - val->intval = 0; - break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - if (!fg_get_vbatt_status(chip, &vbatt_low_sts)) - val->intval = (int)vbatt_low_sts; - else - val->intval = 1; - break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - val->intval = chip->nom_cap_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_FULL: - val->intval = chip->learning_data.learned_cc_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW: - val->intval = chip->learning_data.cc_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW_RAW: - val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE); - break; - case POWER_SUPPLY_PROP_HI_POWER: - val->intval = !!chip->bcl_lpm_disabled; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int correction_times[] = { - 1470, - 2940, - 4410, - 5880, - 7350, - 8820, - 10290, - 11760, - 13230, - 14700, - 16170, - 17640, - 19110, - 20580, - 22050, - 23520, - 24990, - 26460, - 27930, - 29400, - 30870, - 32340, - 33810, - 35280, - 36750, - 38220, - 39690, - 41160, - 42630, - 44100, - 45570, - 47040, -}; - -static int correction_factors[] = { - 1000000, - 1007874, - 1015789, - 1023745, - 1031742, - 1039780, - 1047859, - 1055979, - 1064140, - 1072342, - 1080584, - 1088868, - 1097193, - 1105558, - 1113964, - 1122411, - 1130899, - 1139427, - 1147996, - 1156606, - 1165256, - 1173947, - 1182678, - 1191450, - 1200263, - 1209115, - 1218008, - 1226942, - 1235915, - 1244929, - 1253983, - 1263076, -}; - -#define FG_CONVERSION_FACTOR (64198531LL) -static int iavg_3b_to_uah(u8 *buffer, int delta_ms) -{ - int64_t val, i_filtered; - int i, correction_factor; - - for (i = 0; i < ARRAY_SIZE(correction_times); i++) { - if (correction_times[i] > delta_ms) - break; - } - if (i >= ARRAY_SIZE(correction_times)) { - if (fg_debug_mask & FG_STATUS) - pr_info("fuel gauge took more than 32 cycles\n"); - i = ARRAY_SIZE(correction_times) - 1; - } - correction_factor = correction_factors[i]; - if (fg_debug_mask & FG_STATUS) - pr_info("delta_ms = %d, cycles = %d, correction = %d\n", - delta_ms, i, correction_factor); - val = buffer[2] << 16 | buffer[1] << 8 | buffer[0]; - /* convert val from signed 24b to signed 64b */ - i_filtered = (val << 40) >> 40; - val = i_filtered * correction_factor; - val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR); - if (fg_debug_mask & FG_STATUS) - pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n", - i_filtered, i_filtered, val); - - return val; -} - -static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip) -{ - int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - - if (batt_temp > chip->learning_data.max_temp - || batt_temp < chip->learning_data.min_temp) { - if (fg_debug_mask & FG_AGING) - pr_info("temp (%d) out of range [%d, %d], aborting\n", - batt_temp, - chip->learning_data.min_temp, - chip->learning_data.max_temp); - return false; - } - return true; -} - -static void fg_cap_learning_stop(struct fg_chip *chip) -{ - chip->learning_data.cc_uah = 0; - chip->learning_data.active = false; -} - -#define I_FILTERED_REG 0x584 -static void fg_cap_learning_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - fg_cap_learning_work); - u8 i_filtered[3], data[3]; - int rc, cc_uah, delta_ms; - ktime_t now_kt, delta_kt; - - mutex_lock(&chip->learning_data.learning_lock); - if (!chip->learning_data.active) - goto fail; - if (!fg_is_temperature_ok_for_learning(chip)) { - fg_cap_learning_stop(chip); - goto fail; - } - - if (chip->wa_flag & USE_CC_SOC_REG) { - mutex_unlock(&chip->learning_data.learning_lock); - fg_relax(&chip->capacity_learning_wakeup_source); - return; - } - - fg_mem_lock(chip); - - rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to read i_filtered: %d\n", rc); - fg_mem_release(chip); - goto fail; - } - memset(data, 0, 3); - rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to clear i_filtered: %d\n", rc); - fg_mem_release(chip); - goto fail; - } - fg_mem_release(chip); - - now_kt = ktime_get_boottime(); - delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp); - chip->learning_data.time_stamp = now_kt; - - delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000); - - cc_uah = iavg_3b_to_uah(i_filtered, delta_ms); - chip->learning_data.cc_uah -= cc_uah; - if (fg_debug_mask & FG_AGING) - pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah); - -fail: - mutex_unlock(&chip->learning_data.learning_lock); - return; - -} - -#define CC_SOC_BASE_REG 0x5BC -#define CC_SOC_OFFSET 3 -#define CC_SOC_MAGNITUDE_MASK 0x1FFFFFFF -#define CC_SOC_NEGATIVE_BIT BIT(29) -static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc) -{ - int rc; - u8 reg[4]; - unsigned int temp, magnitude; - - rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0); - if (rc) { - pr_err("Failed to read CC_SOC_REG rc=%d\n", rc); - return rc; - } - - temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0]; - magnitude = temp & CC_SOC_MAGNITUDE_MASK; - if (temp & CC_SOC_NEGATIVE_BIT) - *cc_soc = -1 * (~magnitude + 1); - else - *cc_soc = magnitude; - - return 0; -} - -static int fg_cap_learning_process_full_data(struct fg_chip *chip) -{ - int cc_pc_val, rc = -EINVAL; - unsigned int cc_soc_delta_pc; - int64_t delta_cc_uah; - - if (!chip->learning_data.active) - goto fail; - - if (!fg_is_temperature_ok_for_learning(chip)) { - fg_cap_learning_stop(chip); - goto fail; - } - - rc = fg_get_cc_soc(chip, &cc_pc_val); - if (rc) { - pr_err("failed to get CC_SOC, stopping capacity learning\n"); - fg_cap_learning_stop(chip); - goto fail; - } - - cc_soc_delta_pc = DIV_ROUND_CLOSEST( - abs(cc_pc_val - chip->learning_data.init_cc_pc_val) - * 100, FULL_PERCENT_28BIT); - - delta_cc_uah = div64_s64( - chip->learning_data.learned_cc_uah * cc_soc_delta_pc, - 100); - chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah; - - if (fg_debug_mask & FG_AGING) - pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n", - cc_pc_val, cc_soc_delta_pc, - chip->learning_data.cc_uah); - - return 0; - -fail: - return rc; -} - -#define FG_CAP_LEARNING_INTERVAL_NS 30000000000 -static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm, - ktime_t now) -{ - struct fg_chip *chip = container_of(alarm, struct fg_chip, - fg_cap_learning_alarm); - - if (chip->learning_data.active) { - if (fg_debug_mask & FG_AGING) - pr_info("alarm fired\n"); - schedule_work(&chip->fg_cap_learning_work); - alarm_forward_now(alarm, - ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS)); - return ALARMTIMER_RESTART; - } - if (fg_debug_mask & FG_AGING) - pr_info("alarm misfired\n"); - return ALARMTIMER_NORESTART; -} - -#define FG_AGING_STORAGE_REG 0x5E4 -#define ACTUAL_CAPACITY_REG 0x578 -#define MAH_TO_SOC_CONV_REG 0x4A0 -#define CC_SOC_COEFF_OFFSET 0 -#define ACTUAL_CAPACITY_OFFSET 2 -#define MAH_TO_SOC_CONV_CS_OFFSET 0 -static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah) -{ - int rc; - int64_t cc_to_soc_coeff, mah_to_soc; - u8 data[2]; - - rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2, - ACTUAL_CAPACITY_OFFSET, 0); - if (rc) { - pr_err("Failed to store actual capacity: %d\n", rc); - return rc; - } - - rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2, - MAH_TO_SOC_CONV_CS_OFFSET, 0); - if (rc) { - pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc); - } else { - mah_to_soc = data[1] << 8 | data[0]; - mah_to_soc *= MICRO_UNIT; - cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah); - half_float_to_buffer(cc_to_soc_coeff, data); - rc = fg_mem_write(chip, (u8 *)data, - ACTUAL_CAPACITY_REG, 2, - CC_SOC_COEFF_OFFSET, 0); - if (rc) - pr_err("Failed to write cc_soc_coeff_offset: %d\n", - rc); - else if (fg_debug_mask & FG_AGING) - pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n", - cc_to_soc_coeff, data[0], data[1]); - } - return rc; -} - -static void fg_cap_learning_load_data(struct fg_chip *chip) -{ - int16_t cc_mah; - int64_t old_cap = chip->learning_data.learned_cc_uah; - int rc; - - rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to load aged capacity: %d\n", rc); - } else { - chip->learning_data.learned_cc_uah = cc_mah * 1000; - if (fg_debug_mask & FG_AGING) - pr_info("learned capacity %lld-> %lld/%x uah\n", - old_cap, - chip->learning_data.learned_cc_uah, - cc_mah); - } -} - -static void fg_cap_learning_save_data(struct fg_chip *chip) -{ - int16_t cc_mah; - int rc; - - cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000); - - rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0); - if (rc) - pr_err("Failed to store aged capacity: %d\n", rc); - else if (fg_debug_mask & FG_AGING) - pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n", - chip->learning_data.learned_cc_uah, - cc_mah, cc_mah); - - if (chip->learning_data.feedback_on) { - rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah); - if (rc) - pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc); - } -} - -static void fg_cap_learning_post_process(struct fg_chip *chip) -{ - int64_t max_inc_val, min_dec_val, old_cap; - - max_inc_val = chip->learning_data.learned_cc_uah - * (1000 + chip->learning_data.max_increment); - do_div(max_inc_val, 1000); - - min_dec_val = chip->learning_data.learned_cc_uah - * (1000 - chip->learning_data.max_decrement); - do_div(min_dec_val, 1000); - - old_cap = chip->learning_data.learned_cc_uah; - if (chip->learning_data.cc_uah > max_inc_val) - chip->learning_data.learned_cc_uah = max_inc_val; - else if (chip->learning_data.cc_uah < min_dec_val) - chip->learning_data.learned_cc_uah = min_dec_val; - else - chip->learning_data.learned_cc_uah = - chip->learning_data.cc_uah; - - fg_cap_learning_save_data(chip); - if (fg_debug_mask & FG_AGING) - pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n", - chip->learning_data.cc_uah, - old_cap, chip->learning_data.learned_cc_uah); -} - -static int get_vbat_est_diff(struct fg_chip *chip) -{ - return abs(fg_data[FG_DATA_VOLTAGE].value - - fg_data[FG_DATA_CPRED_VOLTAGE].value); -} - -#define CBITS_INPUT_FILTER_REG 0x4B4 -#define IBATTF_TAU_MASK 0x38 -#define IBATTF_TAU_99_S 0x30 -static int fg_cap_learning_check(struct fg_chip *chip) -{ - u8 data[4]; - int rc = 0, battery_soc, cc_pc_val; - int vbat_est_diff, vbat_est_thr_uv; - unsigned int cc_pc_100 = FULL_PERCENT_28BIT; - - mutex_lock(&chip->learning_data.learning_lock); - if (chip->status == POWER_SUPPLY_STATUS_CHARGING - && !chip->learning_data.active - && chip->batt_aging_mode == FG_AGING_CC) { - if (chip->learning_data.learned_cc_uah == 0) { - if (fg_debug_mask & FG_AGING) - pr_info("no capacity, aborting\n"); - goto fail; - } - - if (!fg_is_temperature_ok_for_learning(chip)) - goto fail; - - fg_mem_lock(chip); - if (!chip->learning_data.feedback_on) { - vbat_est_diff = get_vbat_est_diff(chip); - vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv; - if (vbat_est_diff >= vbat_est_thr_uv && - vbat_est_thr_uv > 0) { - if (fg_debug_mask & FG_AGING) - pr_info("vbat_est_diff (%d) < threshold (%d)\n", - vbat_est_diff, vbat_est_thr_uv); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - } - battery_soc = get_battery_soc_raw(chip); - if (fg_debug_mask & FG_AGING) - pr_info("checking battery soc (%d vs %d)\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.max_start_soc); - /* check if the battery is low enough to start soc learning */ - if (battery_soc * 100 / FULL_PERCENT_3B - > chip->learning_data.max_start_soc) { - if (fg_debug_mask & FG_AGING) - pr_info("battery soc too low (%d < %d), aborting\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.max_start_soc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - - /* set the coulomb counter to a percentage of the capacity */ - chip->learning_data.cc_uah = div64_s64( - (chip->learning_data.learned_cc_uah * battery_soc), - FULL_PERCENT_3B); - - /* Use CC_SOC_REG based capacity learning */ - if (chip->wa_flag & USE_CC_SOC_REG) { - fg_mem_release(chip); - /* SW_CC_SOC based capacity learning */ - if (fg_get_cc_soc(chip, &cc_pc_val)) { - pr_err("failed to get CC_SOC, stop capacity learning\n"); - fg_cap_learning_stop(chip); - goto fail; - } - - chip->learning_data.init_cc_pc_val = cc_pc_val; - chip->learning_data.active = true; - if (fg_debug_mask & FG_AGING) - pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n", - chip->learning_data.init_cc_pc_val); - } else { - rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG, - IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0); - if (rc) { - pr_err("Failed to write IF IBAT Tau: %d\n", - rc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - - /* clear the i_filtered register */ - memset(data, 0, 4); - rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to clear i_filtered: %d\n", rc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - fg_mem_release(chip); - chip->learning_data.time_stamp = ktime_get_boottime(); - chip->learning_data.active = true; - - if (fg_debug_mask & FG_AGING) - pr_info("cap learning started, soc = %d cc_uah = %lld\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.cc_uah); - alarm_start_relative(&chip->fg_cap_learning_alarm, - ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS)); - } - } else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING) - && chip->learning_data.active) { - if (fg_debug_mask & FG_AGING) - pr_info("capacity learning stopped\n"); - if (!(chip->wa_flag & USE_CC_SOC_REG)) - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - - if (chip->status == POWER_SUPPLY_STATUS_FULL) { - if (chip->wa_flag & USE_CC_SOC_REG) { - rc = fg_cap_learning_process_full_data(chip); - if (rc) { - fg_cap_learning_stop(chip); - goto fail; - } - /* reset SW_CC_SOC register to 100% */ - rc = fg_mem_write(chip, (u8 *)&cc_pc_100, - CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0); - if (rc) - pr_err("Failed to reset CC_SOC_REG rc=%d\n", - rc); - } - fg_cap_learning_post_process(chip); - } - - fg_cap_learning_stop(chip); - } - -fail: - mutex_unlock(&chip->learning_data.learning_lock); - return rc; -} - -static bool is_usb_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - if (!chip->usb_psy) - chip->usb_psy = power_supply_get_by_name("usb"); - - if (chip->usb_psy) - power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &prop); - return prop.intval != 0; -} - -static bool is_dc_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - if (!chip->dc_psy) - chip->dc_psy = power_supply_get_by_name("dc"); - - if (chip->dc_psy) - power_supply_get_property(chip->dc_psy, - POWER_SUPPLY_PROP_PRESENT, &prop); - return prop.intval != 0; -} - -static bool is_input_present(struct fg_chip *chip) -{ - return is_usb_present(chip) || is_dc_present(chip); -} - -static bool is_otg_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - - if (!chip->usb_psy) - chip->usb_psy = power_supply_get_by_name("usb"); - - if (chip->usb_psy) - power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_USB_OTG, &prop); - return prop.intval != 0; -} - -static bool is_charger_available(struct fg_chip *chip) -{ - if (!chip->batt_psy_name) - return false; - - if (!chip->batt_psy) - chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name); - - if (!chip->batt_psy) - return false; - - return true; -} - -static int set_prop_enable_charging(struct fg_chip *chip, bool enable) -{ - int rc = 0; - union power_supply_propval ret = {enable, }; - - if (!is_charger_available(chip)) { - pr_err("Charger not available yet!\n"); - return -EINVAL; - } - - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, - &ret); - if (rc) { - pr_err("couldn't configure batt chg %d\n", rc); - return rc; - } - - chip->charging_disabled = !enable; - if (fg_debug_mask & FG_STATUS) - pr_info("%sabling charging\n", enable ? "en" : "dis"); - - return rc; -} - -#define MAX_BATTERY_CC_SOC_CAPACITY 150 -static void status_change_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - status_change_work); - unsigned long current_time = 0; - int cc_soc, rc, capacity = get_prop_capacity(chip); - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - if (chip->status == POWER_SUPPLY_STATUS_FULL) { - if (capacity >= 99 && chip->hold_soc_while_full - && chip->health == POWER_SUPPLY_HEALTH_GOOD) { - if (fg_debug_mask & FG_STATUS) - pr_info("holding soc at 100\n"); - chip->charge_full = true; - } else if (fg_debug_mask & FG_STATUS) { - pr_info("terminated charging at %d/0x%02x\n", - capacity, get_monotonic_soc_raw(chip)); - } - } - if (chip->status == POWER_SUPPLY_STATUS_FULL || - chip->status == POWER_SUPPLY_STATUS_CHARGING) { - if (!chip->vbat_low_irq_enabled) { - enable_irq(chip->batt_irq[VBATT_LOW].irq); - enable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = true; - } - if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100) - fg_configure_soc(chip); - } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) { - if (chip->vbat_low_irq_enabled) { - disable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - } - } - fg_cap_learning_check(chip); - schedule_work(&chip->update_esr_work); - - if (chip->wa_flag & USE_CC_SOC_REG) { - if (fg_get_cc_soc(chip, &cc_soc)) { - pr_err("failed to get CC_SOC\n"); - return; - } - } - - if (chip->prev_status != chip->status && chip->last_sram_update_time) { - get_current_time(¤t_time); - /* - * When charging status changes, update SRAM parameters if it - * was not updated before 5 seconds from now. - */ - if (chip->last_sram_update_time + 5 < current_time) { - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work(&chip->update_sram_data, - msecs_to_jiffies(0)); - } - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - if ((chip->wa_flag & USE_CC_SOC_REG) && - chip->bad_batt_detection_en && - chip->status == POWER_SUPPLY_STATUS_CHARGING) { - chip->sw_cc_soc_data.init_sys_soc = capacity; - chip->sw_cc_soc_data.init_cc_soc = cc_soc; - if (fg_debug_mask & FG_STATUS) - pr_info(" Init_sys_soc %d init_cc_soc %d\n", - chip->sw_cc_soc_data.init_sys_soc, - chip->sw_cc_soc_data.init_cc_soc); - } - } - if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en - && chip->safety_timer_expired) { - chip->sw_cc_soc_data.delta_soc = - DIV_ROUND_CLOSEST(abs(cc_soc - - chip->sw_cc_soc_data.init_cc_soc) - * 100, FULL_PERCENT_28BIT); - chip->sw_cc_soc_data.full_capacity = - chip->sw_cc_soc_data.delta_soc + - chip->sw_cc_soc_data.init_sys_soc; - pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n", - chip->sw_cc_soc_data.init_sys_soc, - chip->sw_cc_soc_data.init_cc_soc, cc_soc, - chip->sw_cc_soc_data.delta_soc, - chip->sw_cc_soc_data.full_capacity); - /* - * If sw_cc_soc capacity greater than 150, then it's a bad - * battery. else, reset timer and restart charging. - */ - if (chip->sw_cc_soc_data.full_capacity > - MAX_BATTERY_CC_SOC_CAPACITY) { - pr_info("Battery possibly damaged, do not restart charging\n"); - } else { - pr_info("Reset safety-timer and restart charging\n"); - rc = set_prop_enable_charging(chip, false); - if (rc) { - pr_err("failed to disable charging %d\n", rc); - return; - } - - chip->safety_timer_expired = false; - msleep(200); - - rc = set_prop_enable_charging(chip, true); - if (rc) { - pr_err("failed to enable charging %d\n", rc); - return; - } - } - } -} - -/* - * Check for change in the status of input or OTG and schedule - * IADC gain compensation work. - */ -static void check_gain_compensation(struct fg_chip *chip) -{ - bool input_present = is_input_present(chip); - bool otg_present = is_otg_present(chip); - - if ((chip->wa_flag & IADC_GAIN_COMP_WA) - && ((chip->input_present ^ input_present) - || (chip->otg_present ^ otg_present))) { - fg_stay_awake(&chip->gain_comp_wakeup_source); - chip->input_present = input_present; - chip->otg_present = otg_present; - cancel_work_sync(&chip->gain_comp_work); - schedule_work(&chip->gain_comp_work); - } -} - -static void fg_hysteresis_config(struct fg_chip *chip) -{ - int hard_hot = 0, hard_cold = 0; - - hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT); - hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD); - if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) { - /* turn down the hard hot threshold */ - chip->batt_hot = true; - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot - chip->hot_hysteresis); - if (fg_debug_mask & FG_STATUS) - pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n", - hard_hot, hard_hot - chip->hot_hysteresis); - } else if (chip->health == POWER_SUPPLY_HEALTH_COLD && - !chip->batt_cold) { - /* turn up the hard cold threshold */ - chip->batt_cold = true; - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold + chip->cold_hysteresis); - if (fg_debug_mask & FG_STATUS) - pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n", - hard_cold, hard_cold + chip->hot_hysteresis); - } else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT && - chip->batt_hot) { - /* restore the hard hot threshold */ - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot + chip->hot_hysteresis); - chip->batt_hot = !chip->batt_hot; - if (fg_debug_mask & FG_STATUS) - pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n", - hard_hot, - hard_hot + chip->hot_hysteresis); - } else if (chip->health != POWER_SUPPLY_HEALTH_COLD && - chip->batt_cold) { - /* restore the hard cold threshold */ - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold - chip->cold_hysteresis); - chip->batt_cold = !chip->batt_cold; - if (fg_debug_mask & FG_STATUS) - pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n", - hard_cold, - hard_cold - chip->cold_hysteresis); - } -} - -#define BATT_INFO_STS(base) (base + 0x09) -#define JEITA_HARD_HOT_RT_STS BIT(6) -#define JEITA_HARD_COLD_RT_STS BIT(5) -static int fg_init_batt_temp_state(struct fg_chip *chip) -{ - int rc = 0; - u8 batt_info_sts; - int hard_hot = 0, hard_cold = 0; - - /* - * read the batt_info_sts register to parse battery's - * initial status and do hysteresis config accordingly. - */ - rc = fg_read(chip, &batt_info_sts, - BATT_INFO_STS(chip->batt_base), 1); - if (rc) { - pr_err("failed to read batt info sts, rc=%d\n", rc); - return rc; - } - - hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT); - hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD); - chip->batt_hot = - (batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false; - chip->batt_cold = - (batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false; - if (chip->batt_hot || chip->batt_cold) { - if (chip->batt_hot) { - chip->health = POWER_SUPPLY_HEALTH_OVERHEAT; - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot - chip->hot_hysteresis); - } else { - chip->health = POWER_SUPPLY_HEALTH_COLD; - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold + chip->cold_hysteresis); - } - } - - return rc; -} - -static int fg_power_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *val) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - int rc = 0, unused; - - switch (psp) { - case POWER_SUPPLY_PROP_COOL_TEMP: - rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval); - break; - case POWER_SUPPLY_PROP_WARM_TEMP: - rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval); - break; - case POWER_SUPPLY_PROP_UPDATE_NOW: - if (val->intval) - update_sram_data(chip, &unused); - break; - case POWER_SUPPLY_PROP_STATUS: - chip->prev_status = chip->status; - chip->status = val->intval; - schedule_work(&chip->status_change_work); - check_gain_compensation(chip); - break; - case POWER_SUPPLY_PROP_HEALTH: - chip->health = val->intval; - if (chip->health == POWER_SUPPLY_HEALTH_GOOD) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - - if (chip->jeita_hysteresis_support) - fg_hysteresis_config(chip); - break; - case POWER_SUPPLY_PROP_CHARGE_DONE: - chip->charge_done = val->intval; - if (!chip->resume_soc_lowered) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) { - chip->cyc_ctr.id = val->intval; - } else { - pr_err("rejecting invalid cycle_count_id = %d\n", - val->intval); - rc = -EINVAL; - } - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED: - chip->safety_timer_expired = val->intval; - schedule_work(&chip->status_change_work); - break; - case POWER_SUPPLY_PROP_HI_POWER: - if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) { - chip->bcl_lpm_disabled = !!val->intval; - schedule_work(&chip->bcl_hi_power_work); - } - break; - default: - return -EINVAL; - }; - - return rc; -}; - -static int fg_property_is_writeable(struct power_supply *psy, - enum power_supply_property psp) -{ - switch (psp) { - case POWER_SUPPLY_PROP_COOL_TEMP: - case POWER_SUPPLY_PROP_WARM_TEMP: - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - return 1; - default: - break; - } - - return 0; -} - -#define SRAM_DUMP_START 0x400 -#define SRAM_DUMP_LEN 0x200 -static void dump_sram(struct work_struct *work) -{ - int i, rc; - u8 *buffer, rt_sts; - char str[16]; - struct fg_chip *chip = container_of(work, - struct fg_chip, - dump_sram); - - buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL); - if (buffer == NULL) { - pr_err("Can't allocate buffer\n"); - return; - } - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - else - pr_info("soc rt_sts: 0x%x\n", rt_sts); - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - else - pr_info("batt rt_sts: 0x%x\n", rt_sts); - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->mem_base), rc); - else - pr_info("memif rt_sts: 0x%x\n", rt_sts); - - rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0); - if (rc) { - pr_err("dump failed: rc = %d\n", rc); - return; - } - - for (i = 0; i < SRAM_DUMP_LEN; i += 4) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4); - pr_info("%03X %s\n", SRAM_DUMP_START + i, str); - } - devm_kfree(chip->dev, buffer); -} - -#define MAXRSCHANGE_REG 0x434 -#define ESR_VALUE_OFFSET 1 -#define ESR_STRICT_VALUE 0x4120391F391F3019 -#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67 -static void update_esr_value(struct work_struct *work) -{ - union power_supply_propval prop = {0, }; - u64 esr_value; - int rc = 0; - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_esr_work); - - if (!is_charger_available(chip)) - return; - - power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_TYPE, &prop); - - if (!chip->esr_strict_filter) { - if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER && - chip->status == POWER_SUPPLY_STATUS_CHARGING) || - (chip->status == POWER_SUPPLY_STATUS_FULL)) { - esr_value = ESR_STRICT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, - MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write strict ESR value rc=%d\n", - rc); - else - chip->esr_strict_filter = true; - } - } else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER && - chip->status == POWER_SUPPLY_STATUS_CHARGING) || - (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) { - esr_value = ESR_DEFAULT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write default ESR value rc=%d\n", rc); - else - chip->esr_strict_filter = false; - } -} - -#define TEMP_COUNTER_REG 0x580 -#define VBAT_FILTERED_OFFSET 1 -#define GAIN_REG 0x424 -#define GAIN_OFFSET 1 -#define K_VCOR_REG 0x484 -#define DEF_GAIN_OFFSET 2 -#define PICO_UNIT 0xE8D4A51000LL -#define ATTO_UNIT 0xDE0B6B3A7640000LL -#define VBAT_REF 3800000 - -/* - * IADC Gain compensation steps: - * If Input/OTG absent: - * - read VBAT_FILTERED, KVCOR, GAIN - * - calculate the gain compensation using following formula: - * gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1; - * else - * - reset to the default gain compensation - */ -static void iadc_gain_comp_work(struct work_struct *work) -{ - u8 reg[4]; - int rc; - uint64_t vbat_filtered; - int64_t gain, kvcor, temp, numerator; - struct fg_chip *chip = container_of(work, struct fg_chip, - gain_comp_work); - bool input_present = is_input_present(chip); - bool otg_present = is_otg_present(chip); - - if (!chip->init_done) - goto done; - - if (!input_present && !otg_present) { - /* read VBAT_FILTERED */ - rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3, - VBAT_FILTERED_OFFSET, 0); - if (rc) { - pr_err("Failed to read VBAT: rc=%d\n", rc); - goto done; - } - temp = (reg[2] << 16) | (reg[1] << 8) | reg[0]; - vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR, - LSB_24B_DENMTR); - - /* read K_VCOR */ - rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to KVCOR rc=%d\n", rc); - goto done; - } - kvcor = half_float(reg); - - /* calculate gain */ - numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain) - * (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF)) - - ATTO_UNIT; - gain = div64_s64(numerator, PICO_UNIT); - - /* write back gain */ - half_float_to_buffer(gain, reg); - rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write gain reg rc=%d\n", rc); - goto done; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]); - chip->iadc_comp_data.gain_active = true; - } else { - /* reset gain register */ - rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg, - GAIN_REG, 2, GAIN_OFFSET, 0); - if (rc) { - pr_err("unable to write gain comp: %d\n", rc); - goto done; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("IADC gain reset [%x %x]\n", - chip->iadc_comp_data.dfl_gain_reg[1], - chip->iadc_comp_data.dfl_gain_reg[0]); - chip->iadc_comp_data.gain_active = false; - } - -done: - fg_relax(&chip->gain_comp_wakeup_source); -} - -#define BATT_MISSING_STS BIT(6) -static bool is_battery_missing(struct fg_chip *chip) -{ - int rc; - u8 fg_batt_sts; - - rc = fg_read(chip, &fg_batt_sts, - INT_RT_STS(chip->batt_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - return false; - } - - return (fg_batt_sts & BATT_MISSING_STS) ? true : false; -} - -#define SOC_FIRST_EST_DONE BIT(5) -static bool is_first_est_done(struct fg_chip *chip) -{ - int rc; - u8 fg_soc_sts; - - rc = fg_read(chip, &fg_soc_sts, - INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - return false; - } - - return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false; -} - -static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - int rc; - bool vbatt_low_sts; - - if (fg_debug_mask & FG_IRQS) - pr_info("vbatt-low triggered\n"); - - if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { - rc = fg_get_vbatt_status(chip, &vbatt_low_sts); - if (rc) { - pr_err("error in reading vbatt_status, rc:%d\n", rc); - goto out; - } - if (!vbatt_low_sts && chip->vbat_low_irq_enabled) { - if (fg_debug_mask & FG_IRQS) - pr_info("disabling vbatt_low irq\n"); - disable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - } - } - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); -out: - return IRQ_HANDLED; -} - -static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - bool batt_missing = is_battery_missing(chip); - - if (batt_missing) { - chip->battery_missing = true; - chip->profile_loaded = false; - chip->batt_type = default_batt_type; - mutex_lock(&chip->cyc_ctr.lock); - if (fg_debug_mask & FG_IRQS) - pr_info("battery missing, clearing cycle counters\n"); - clear_cycle_counter(chip); - mutex_unlock(&chip->cyc_ctr.lock); - } else { - if (!chip->use_otp_profile) { - reinit_completion(&chip->batt_id_avail); - reinit_completion(&chip->first_soc_done); - schedule_delayed_work(&chip->batt_profile_init, 0); - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(0)); - } else { - chip->battery_missing = false; - } - } - - if (fg_debug_mask & FG_IRQS) - pr_info("batt-missing triggered: %s\n", - batt_missing ? "missing" : "present"); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - return IRQ_HANDLED; -} - -static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 mem_if_sts; - int rc; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return IRQ_HANDLED; - } - - if (fg_check_sram_access(chip)) { - if ((fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("sram access granted\n"); - reinit_completion(&chip->sram_access_revoked); - complete_all(&chip->sram_access_granted); - } else { - if ((fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("sram access revoked\n"); - complete_all(&chip->sram_access_revoked); - } - - if (!rc && (fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("mem_if sts 0x%02x\n", mem_if_sts); - - return IRQ_HANDLED; -} - -static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 soc_rt_sts; - int rc; - - rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - } - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered 0x%x\n", soc_rt_sts); - - schedule_work(&chip->battery_age_work); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - if (chip->rslow_comp.chg_rs_to_rslow > 0 && - chip->rslow_comp.chg_rslow_comp_c1 > 0 && - chip->rslow_comp.chg_rslow_comp_c2 > 0) - schedule_work(&chip->rslow_comp_work); - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - schedule_work(&chip->update_esr_work); - if (chip->charge_full) - schedule_work(&chip->charge_full_work); - if (chip->wa_flag & IADC_GAIN_COMP_WA - && chip->iadc_comp_data.gain_active) { - fg_stay_awake(&chip->gain_comp_wakeup_source); - schedule_work(&chip->gain_comp_work); - } - - if (chip->wa_flag & USE_CC_SOC_REG - && chip->learning_data.active) { - fg_stay_awake(&chip->capacity_learning_wakeup_source); - schedule_work(&chip->fg_cap_learning_work); - } - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - return IRQ_HANDLED; -} - -#define FG_EMPTY_DEBOUNCE_MS 1500 -static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 soc_rt_sts; - int rc; - - rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - goto done; - } - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered 0x%x\n", soc_rt_sts); - if (fg_is_batt_empty(chip)) { - fg_stay_awake(&chip->empty_check_wakeup_source); - schedule_delayed_work(&chip->check_empty_work, - msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS)); - } else { - chip->soc_empty = false; - } - -done: - return IRQ_HANDLED; -} - -static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered\n"); - - if (fg_est_dump) - schedule_work(&chip->dump_sram); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - complete_all(&chip->first_soc_done); - - return IRQ_HANDLED; -} - -static void fg_external_power_changed(struct power_supply *psy) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - - if (is_input_present(chip) && chip->rslow_comp.active && - chip->rslow_comp.chg_rs_to_rslow > 0 && - chip->rslow_comp.chg_rslow_comp_c1 > 0 && - chip->rslow_comp.chg_rslow_comp_c2 > 0) - schedule_work(&chip->rslow_comp_work); - if (!is_input_present(chip) && chip->resume_soc_lowered) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - if (!is_input_present(chip) && chip->charge_full) - schedule_work(&chip->charge_full_work); -} - -static void set_resume_soc_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - set_resume_soc_work); - int rc, resume_soc_raw; - - if (is_input_present(chip) && !chip->resume_soc_lowered) { - if (!chip->charge_done) - goto done; - resume_soc_raw = get_monotonic_soc_raw(chip) - - (0xFF - settings[FG_MEM_RESUME_SOC].value); - if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - goto done; - } - if (fg_debug_mask & FG_STATUS) { - pr_info("resume soc lowered to 0x%02x\n", - resume_soc_raw); - } - } else if (settings[FG_MEM_RESUME_SOC].value > 0) { - pr_err("bad resume soc 0x%02x\n", resume_soc_raw); - } - chip->charge_done = false; - chip->resume_soc_lowered = true; - } else if (chip->resume_soc_lowered && (!is_input_present(chip) - || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { - resume_soc_raw = settings[FG_MEM_RESUME_SOC].value; - if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - goto done; - } - if (fg_debug_mask & FG_STATUS) { - pr_info("resume soc set to 0x%02x\n", - resume_soc_raw); - } - } else if (settings[FG_MEM_RESUME_SOC].value > 0) { - pr_err("bad resume soc 0x%02x\n", resume_soc_raw); - } - chip->resume_soc_lowered = false; - } -done: - fg_relax(&chip->resume_soc_wakeup_source); -} - - -#define OCV_COEFFS_START_REG 0x4C0 -#define OCV_JUNCTION_REG 0x4D8 -#define NOM_CAP_REG 0x4F4 -#define CUTOFF_VOLTAGE_REG 0x40C -#define RSLOW_CFG_REG 0x538 -#define RSLOW_CFG_OFFSET 2 -#define RSLOW_THRESH_REG 0x52C -#define RSLOW_THRESH_OFFSET 0 -#define TEMP_RS_TO_RSLOW_OFFSET 2 -#define RSLOW_COMP_REG 0x528 -#define RSLOW_COMP_C1_OFFSET 0 -#define RSLOW_COMP_C2_OFFSET 2 -static int populate_system_data(struct fg_chip *chip) -{ - u8 buffer[24]; - int rc, i; - int16_t cc_mah; - - fg_mem_lock(chip); - rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0); - if (rc) { - pr_err("Failed to read ocv coefficients: %d\n", rc); - goto done; - } - for (i = 0; i < 12; i += 1) - chip->ocv_coeffs[i] = half_float(buffer + (i * 2)); - if (fg_debug_mask & FG_AGING) { - pr_info("coeffs1 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[0], chip->ocv_coeffs[1], - chip->ocv_coeffs[2], chip->ocv_coeffs[3]); - pr_info("coeffs2 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[4], chip->ocv_coeffs[5], - chip->ocv_coeffs[6], chip->ocv_coeffs[7]); - pr_info("coeffs3 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[8], chip->ocv_coeffs[9], - chip->ocv_coeffs[10], chip->ocv_coeffs[11]); - } - rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0); - chip->ocv_junction_p1p2 = buffer[0] * 100 / 255; - rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0); - chip->ocv_junction_p2p3 = buffer[0] * 100 / 255; - if (rc) { - pr_err("Failed to read ocv junctions: %d\n", rc); - goto done; - } - rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to read nominal capacitance: %d\n", rc); - goto done; - } - chip->nom_cap_uah = bcap_uah_2b(buffer); - chip->actual_cap_uah = chip->nom_cap_uah; - if (chip->learning_data.learned_cc_uah == 0) { - chip->learning_data.learned_cc_uah = chip->nom_cap_uah; - fg_cap_learning_save_data(chip); - } else if (chip->learning_data.feedback_on) { - cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000); - rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah); - if (rc) - pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc); - } - rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to read cutoff voltage: %d\n", rc); - goto done; - } - chip->cutoff_voltage = voltage_2b(buffer); - if (fg_debug_mask & FG_AGING) - pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n", - chip->cutoff_voltage, chip->nom_cap_uah, - chip->ocv_junction_p1p2, - chip->ocv_junction_p2p3); - - rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow cfg: %d\n", rc); - goto done; - } - chip->rslow_comp.rslow_cfg = buffer[0]; - rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1, - RSLOW_THRESH_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow thresh: %d\n", rc); - goto done; - } - chip->rslow_comp.rslow_thr = buffer[0]; - rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, - RSLOW_THRESH_OFFSET, 0); - if (rc) { - pr_err("unable to read rs to rslow: %d\n", rc); - goto done; - } - memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2); - rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4, - RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow comp: %d\n", rc); - goto done; - } - memcpy(chip->rslow_comp.rslow_comp, buffer, 4); - -done: - fg_mem_release(chip); - return rc; -} - -#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5)) -#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3)) -#define RSLOW_THRESH_FULL_VAL 0xFF -static int fg_rslow_charge_comp_set(struct fg_chip *chip) -{ - int rc; - u8 buffer[2]; - - mutex_lock(&chip->rslow_comp.lock); - fg_mem_lock(chip); - - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg: %d\n", rc); - goto done; - } - rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG, - 0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET); - if (rc) { - pr_err("unable to write rslow thresh: %d\n", rc); - goto done; - } - - half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer); - rc = fg_mem_write(chip, buffer, - TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0); - if (rc) { - pr_err("unable to write rs to rslow: %d\n", rc); - goto done; - } - half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer); - rc = fg_mem_write(chip, buffer, - RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer); - rc = fg_mem_write(chip, buffer, - RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - chip->rslow_comp.active = true; - if (fg_debug_mask & FG_STATUS) - pr_info("Activated rslow charge comp values\n"); - -done: - fg_mem_release(chip); - mutex_unlock(&chip->rslow_comp.lock); - return rc; -} - -#define RSLOW_CFG_ORIG_MASK (BIT(4) | BIT(5)) -static int fg_rslow_charge_comp_clear(struct fg_chip *chip) -{ - u8 reg; - int rc; - - mutex_lock(&chip->rslow_comp.lock); - fg_mem_lock(chip); - - reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK; - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg: %d\n", rc); - goto done; - } - rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG, - 0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET); - if (rc) { - pr_err("unable to write rslow thresh: %d\n", rc); - goto done; - } - - rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow, - TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0); - if (rc) { - pr_err("unable to write rs to rslow: %d\n", rc); - goto done; - } - rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp, - RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - chip->rslow_comp.active = false; - if (fg_debug_mask & FG_STATUS) - pr_info("Cleared rslow charge comp values\n"); - -done: - fg_mem_release(chip); - mutex_unlock(&chip->rslow_comp.lock); - return rc; -} - -static void rslow_comp_work(struct work_struct *work) -{ - int battery_soc_1b; - struct fg_chip *chip = container_of(work, - struct fg_chip, - rslow_comp_work); - - battery_soc_1b = get_battery_soc_raw(chip) >> 16; - if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr - && chip->status == POWER_SUPPLY_STATUS_CHARGING) { - if (!chip->rslow_comp.active) - fg_rslow_charge_comp_set(chip); - } else { - if (chip->rslow_comp.active) - fg_rslow_charge_comp_clear(chip); - } -} - -#define MICROUNITS_TO_ADC_RAW(units) \ - div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR) -static int update_chg_iterm(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_current_raw; - s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value; - - converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000); - data[0] = cpu_to_le16(converted_current_raw) & 0xFF; - data[1] = cpu_to_le16(converted_current_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n", - current_ma, converted_current_raw, data[0], data[1]); - return fg_mem_write(chip, data, - settings[FG_MEM_CHG_TERM_CURRENT].address, - 2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0); -} - -#define CC_CV_SETPOINT_REG 0x4F8 -#define CC_CV_SETPOINT_OFFSET 0 -static void update_cc_cv_setpoint(struct fg_chip *chip) -{ - int rc; - u8 tmp[2]; - - if (!chip->cc_cv_threshold_mv) - return; - batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp); - rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2, - CC_CV_SETPOINT_OFFSET, 0); - if (rc) { - pr_err("failed to write CC_CV_VOLT rc=%d\n", rc); - return; - } - if (fg_debug_mask & FG_STATUS) - pr_info("Wrote %x %x to address %x for CC_CV setpoint\n", - tmp[0], tmp[1], CC_CV_SETPOINT_REG); -} - -#define CBITS_INPUT_FILTER_REG 0x4B4 -#define CBITS_RMEAS1_OFFSET 1 -#define CBITS_RMEAS2_OFFSET 2 -#define CBITS_RMEAS1_DEFAULT_VAL 0x65 -#define CBITS_RMEAS2_DEFAULT_VAL 0x65 -#define IMPTR_FAST_TIME_SHIFT 1 -#define IMPTR_LONG_TIME_SHIFT (1 << 4) -#define IMPTR_PULSE_CTR_CHG 1 -#define IMPTR_PULSE_CTR_DISCHG (1 << 4) -static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow) -{ - int rc; - u8 cntr[2] = {0, 0}; - u8 val; - - if (slow == chip->imptr_pulse_slow_en) { - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled already\n", - slow ? "en" : "dis"); - return 0; - } - - fg_mem_lock(chip); - - val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) : - CBITS_RMEAS1_DEFAULT_VAL; - rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS1_OFFSET, 0); - if (rc) { - pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc); - goto done; - } - - val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) : - CBITS_RMEAS2_DEFAULT_VAL; - rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS2_OFFSET, 0); - if (rc) { - pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc); - goto done; - } - - if (slow) { - rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4, - COUNTER_IMPTR_OFFSET, 0); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto done; - } - - rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2, - COUNTER_PULSE_OFFSET, 0); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto done; - } - } - - chip->imptr_pulse_slow_en = slow; - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis"); -done: - fg_mem_release(chip); - return rc; -} - -#define CURRENT_DELTA_MIN_REG 0x42C -#define CURRENT_DELTA_MIN_OFFSET 1 -#define SYS_CFG_1_REG 0x4AC -#define SYS_CFG_1_OFFSET 0 -#define CURRENT_DELTA_MIN_DEFAULT 0x16 -#define CURRENT_DELTA_MIN_500MA 0xCD -#define RSLOW_CFG_USE_FIX_RSER_VAL BIT(7) -#define ENABLE_ESR_PULSE_VAL BIT(3) -static int fg_config_esr_extract(struct fg_chip *chip, bool disable) -{ - int rc; - u8 val; - - if (disable == chip->esr_extract_disabled) { - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract already %sabled\n", - disable ? "dis" : "en"); - return 0; - } - - fg_mem_lock(chip); - - val = disable ? CURRENT_DELTA_MIN_500MA : - CURRENT_DELTA_MIN_DEFAULT; - rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1, - CURRENT_DELTA_MIN_OFFSET, 0); - if (rc) { - pr_err("unable to write curr_delta_min rc=%d\n", rc); - goto done; - } - - val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0; - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg rc= %d\n", rc); - goto done; - } - - val = disable ? 0 : ENABLE_ESR_PULSE_VAL; - rc = fg_mem_masked_write(chip, SYS_CFG_1_REG, - ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET); - if (rc) { - pr_err("unable to write sys_cfg_1 rc= %d\n", rc); - goto done; - } - - chip->esr_extract_disabled = disable; - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract is %sabled\n", disable ? "dis" : "en"); -done: - fg_mem_release(chip); - return rc; -} - -#define ESR_EXTRACT_STOP_SOC 2 -#define IMPTR_PULSE_CONFIG_SOC 5 -static void esr_extract_config_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, struct fg_chip, - esr_extract_config_work); - bool input_present = is_input_present(chip); - int capacity = get_prop_capacity(chip); - - if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) { - fg_config_esr_extract(chip, true); - } else if (capacity > ESR_EXTRACT_STOP_SOC) { - fg_config_esr_extract(chip, false); - - if (capacity <= IMPTR_PULSE_CONFIG_SOC) - fg_config_imptr_pulse(chip, true); - else - fg_config_imptr_pulse(chip, false); - } - - fg_relax(&chip->esr_extract_wakeup_source); -} - -#define LOW_LATENCY BIT(6) -#define BATT_PROFILE_OFFSET 0x4C0 -#define PROFILE_INTEGRITY_REG 0x53C -#define PROFILE_INTEGRITY_BIT BIT(0) -#define FIRST_EST_DONE_BIT BIT(5) -#define MAX_TRIES_FIRST_EST 3 -#define FIRST_EST_WAIT_MS 2000 -#define PROFILE_LOAD_TIMEOUT_MS 5000 -static int fg_do_restart(struct fg_chip *chip, bool write_profile) -{ - int rc, ibat_ua; - u8 reg = 0; - u8 buf[2]; - bool tried_once = false; - - if (fg_debug_mask & FG_STATUS) - pr_info("restarting fuel gauge...\n"); - -try_again: - if (write_profile) { - if (!chip->charging_disabled) { - pr_err("Charging not yet disabled!\n"); - return -EINVAL; - } - - ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT); - if (ibat_ua == -EINVAL) { - pr_err("SRAM not updated yet!\n"); - return ibat_ua; - } - - if (ibat_ua < 0) { - pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua); - - if (!tried_once) { - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work(&chip->update_sram_data, - msecs_to_jiffies(0)); - msleep(1000); - tried_once = true; - goto try_again; - } - } - } - - chip->fg_restarting = true; - /* - * save the temperature if the sw rbias control is active so that there - * is no gap of time when there is no valid temperature read after the - * restart - */ - if (chip->sw_rbias_ctrl) { - rc = fg_mem_read(chip, buf, - fg_data[FG_DATA_BATT_TEMP].address, - fg_data[FG_DATA_BATT_TEMP].len, - fg_data[FG_DATA_BATT_TEMP].offset, 0); - if (rc) { - pr_err("failed to read batt temp rc=%d\n", rc); - goto sub_and_fail; - } - } - /* - * release the sram access and configure the correct settings - * before re-requesting access. - */ - mutex_lock(&chip->rw_lock); - fg_release_access(chip); - - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, 0, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto unlock_and_fail; - } - - /* unset the restart bits so the fg doesn't continuously restart */ - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, 0, 1); - if (rc) { - pr_err("failed to unset fg restart: %d\n", rc); - goto unlock_and_fail; - } - - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - LOW_LATENCY, LOW_LATENCY, 1); - if (rc) { - pr_err("failed to set low latency access bit\n"); - goto unlock_and_fail; - } - mutex_unlock(&chip->rw_lock); - - /* read once to get a fg cycle in */ - rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0); - if (rc) { - pr_err("failed to read profile integrity rc=%d\n", rc); - goto fail; - } - - /* - * If this is not the first time a profile has been loaded, sleep for - * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory - */ - if (chip->first_profile_loaded) - msleep(3000); - - mutex_lock(&chip->rw_lock); - fg_release_access(chip); - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1); - if (rc) { - pr_err("failed to set low latency access bit\n"); - goto unlock_and_fail; - } - - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); - - if (write_profile) { - /* write the battery profile */ - rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET, - chip->batt_profile_len, 0, 1); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - goto sub_and_fail; - } - /* write the integrity bits and release access */ - rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG, - PROFILE_INTEGRITY_BIT, - PROFILE_INTEGRITY_BIT, 0); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - goto sub_and_fail; - } - } - - /* decrement the user count so that memory access can be released */ - fg_release_access_if_necessary(chip); - - /* - * make sure that the first estimate has completed - * in case of a hotswap - */ - rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - if (rc <= 0) { - pr_err("transaction timed out rc=%d\n", rc); - rc = -ETIMEDOUT; - goto fail; - } - - /* - * reinitialize the completion so that the driver knows when the restart - * finishes - */ - reinit_completion(&chip->first_soc_done); - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - /* - * set the restart bits so that the next fg cycle will not reload - * the profile - */ - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto fail; - } - - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, reg, 1); - if (rc) { - pr_err("failed to set fg restart: %d\n", rc); - goto fail; - } - - /* wait for the first estimate to complete */ - rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - if (rc <= 0) { - pr_err("transaction timed out rc=%d\n", rc); - rc = -ETIMEDOUT; - goto fail; - } - rc = fg_read(chip, ®, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - goto fail; - } - if ((reg & FIRST_EST_DONE_BIT) == 0) - pr_err("Battery profile reloading failed, no first estimate\n"); - - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, 0, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto fail; - } - /* unset the restart bits so the fg doesn't continuously restart */ - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, 0, 1); - if (rc) { - pr_err("failed to unset fg restart: %d\n", rc); - goto fail; - } - - /* restore the battery temperature reading here */ - if (chip->sw_rbias_ctrl) { - if (fg_debug_mask & FG_STATUS) - pr_info("reloaded 0x%02x%02x into batt temp", - buf[0], buf[1]); - rc = fg_mem_write(chip, buf, - fg_data[FG_DATA_BATT_TEMP].address, - fg_data[FG_DATA_BATT_TEMP].len, - fg_data[FG_DATA_BATT_TEMP].offset, 0); - if (rc) { - pr_err("failed to write batt temp rc=%d\n", rc); - goto fail; - } - } - - /* Enable charging now as the first estimate is done now */ - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - chip->fg_restarting = false; - - if (fg_debug_mask & FG_STATUS) - pr_info("done!\n"); - return 0; - -unlock_and_fail: - mutex_unlock(&chip->rw_lock); - goto fail; -sub_and_fail: - fg_release_access_if_necessary(chip); - goto fail; -fail: - chip->fg_restarting = false; - return -EINVAL; -} - -#define FG_PROFILE_LEN 128 -#define PROFILE_COMPARE_LEN 32 -#define THERMAL_COEFF_ADDR 0x444 -#define THERMAL_COEFF_OFFSET 0x2 -#define BATTERY_PSY_WAIT_MS 2000 -static int fg_batt_profile_init(struct fg_chip *chip) -{ - int rc = 0, ret, len, batt_id; - struct device_node *node = chip->pdev->dev.of_node; - struct device_node *batt_node, *profile_node; - const char *data, *batt_type_str; - bool tried_again = false, vbat_in_range, profiles_same; - u8 reg = 0; - -wait: - fg_stay_awake(&chip->profile_wakeup_source); - ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - pr_debug("interrupted, waiting again\n"); - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("profile loading timed out rc=%d\n", rc); - goto no_profile; - } - - batt_node = of_find_node_by_name(node, "qcom,battery-data"); - if (!batt_node) { - pr_warn("No available batterydata, using OTP defaults\n"); - rc = 0; - goto no_profile; - } - - batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID); - batt_id /= 1000; - if (fg_debug_mask & FG_STATUS) - pr_info("battery id = %dKOhms\n", batt_id); - - profile_node = of_batterydata_get_best_profile(batt_node, batt_id, - fg_batt_type); - if (IS_ERR_OR_NULL(profile_node)) { - rc = PTR_ERR(profile_node); - pr_err("couldn't find profile handle %d\n", rc); - goto no_profile; - } - - /* read rslow compensation values if they're available */ - rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow", - &chip->rslow_comp.chg_rs_to_rslow); - if (rc) { - chip->rslow_comp.chg_rs_to_rslow = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rs to rslow: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1", - &chip->rslow_comp.chg_rslow_comp_c1); - if (rc) { - chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp c1: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2", - &chip->rslow_comp.chg_rslow_comp_c2); - if (rc) { - chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp c2: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr", - &chip->rslow_comp.chg_rslow_comp_thr); - if (rc) { - chip->rslow_comp.chg_rslow_comp_thr = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp thr: %d\n", rc); - } - - rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", - &chip->batt_max_voltage_uv); - - if (rc) - pr_warn("couldn't find battery max voltage\n"); - - /* - * Only configure from profile if fg-cc-cv-threshold-mv is not - * defined in the charger device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,fg-cc-cv-threshold-mv", NULL)) { - of_property_read_u32(profile_node, - "qcom,fg-cc-cv-threshold-mv", - &chip->cc_cv_threshold_mv); - } - - data = of_get_property(profile_node, "qcom,fg-profile-data", &len); - if (!data) { - pr_err("no battery profile loaded\n"); - rc = 0; - goto no_profile; - } - - if (len != FG_PROFILE_LEN) { - pr_err("battery profile incorrect size: %d\n", len); - rc = -EINVAL; - goto no_profile; - } - - rc = of_property_read_string(profile_node, "qcom,battery-type", - &batt_type_str); - if (rc) { - pr_err("Could not find battery data type: %d\n", rc); - rc = 0; - goto no_profile; - } - - if (!chip->batt_profile) - chip->batt_profile = devm_kzalloc(chip->dev, - sizeof(char) * len, GFP_KERNEL); - - if (!chip->batt_profile) { - pr_err("out of memory\n"); - rc = -ENOMEM; - goto no_profile; - } - - rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 1); - if (rc) { - pr_err("failed to read profile integrity rc=%d\n", rc); - goto no_profile; - } - - rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET, - len, 0, 1); - if (rc) { - pr_err("failed to read profile rc=%d\n", rc); - goto no_profile; - } - - /* Check whether the charger is ready */ - if (!is_charger_available(chip)) - goto reschedule; - - /* Disable charging for a FG cycle before calculating vbat_in_range */ - if (!chip->charging_disabled) { - rc = set_prop_enable_charging(chip, false); - if (rc) - pr_err("Failed to disable charging, rc=%d\n", rc); - - goto reschedule; - } - - vbat_in_range = get_vbat_est_diff(chip) - < settings[FG_MEM_VBAT_EST_DIFF].value * 1000; - profiles_same = memcmp(chip->batt_profile, data, - PROFILE_COMPARE_LEN) == 0; - if (reg & PROFILE_INTEGRITY_BIT) { - fg_cap_learning_load_data(chip); - if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { - if (fg_debug_mask & FG_STATUS) - pr_info("Battery profiles same, using default\n"); - if (fg_est_dump) - schedule_work(&chip->dump_sram); - goto done; - } - } else { - pr_info("Battery profile not same, clearing data\n"); - clear_cycle_counter(chip); - chip->learning_data.learned_cc_uah = 0; - } - - if (fg_est_dump) - dump_sram(&chip->dump_sram); - - if ((fg_debug_mask & FG_STATUS) && !vbat_in_range) - pr_info("Vbat out of range: v_current_pred: %d, v:%d\n", - fg_data[FG_DATA_CPRED_VOLTAGE].value, - fg_data[FG_DATA_VOLTAGE].value); - - if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip)) - pr_info("battery empty\n"); - - if ((fg_debug_mask & FG_STATUS) && !profiles_same) - pr_info("profiles differ\n"); - - if (fg_debug_mask & FG_STATUS) { - pr_info("Using new profile\n"); - print_hex_dump(KERN_INFO, "FG: loaded profile: ", - DUMP_PREFIX_NONE, 16, 1, - chip->batt_profile, len, false); - } - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - memcpy(chip->batt_profile, data, len); - - chip->batt_profile_len = len; - - if (fg_debug_mask & FG_STATUS) - print_hex_dump(KERN_INFO, "FG: new profile: ", - DUMP_PREFIX_NONE, 16, 1, chip->batt_profile, - chip->batt_profile_len, false); - - rc = fg_do_restart(chip, true); - if (rc) { - pr_err("restart failed: %d\n", rc); - goto no_profile; - } - - /* - * Only configure from profile if thermal-coefficients is not - * defined in the FG device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,thermal-coefficients", NULL)) { - data = of_get_property(profile_node, - "qcom,thermal-coefficients", &len); - if (data && len == THERMAL_COEFF_N_BYTES) { - memcpy(chip->thermal_coefficients, data, len); - rc = fg_mem_write(chip, chip->thermal_coefficients, - THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES, - THERMAL_COEFF_OFFSET, 0); - if (rc) - pr_err("spmi write failed addr:%03x, ret:%d\n", - THERMAL_COEFF_ADDR, rc); - else if (fg_debug_mask & FG_STATUS) - pr_info("Battery thermal coefficients changed\n"); - } - } - -done: - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - if (fg_batt_type) - chip->batt_type = fg_batt_type; - else - chip->batt_type = batt_type_str; - chip->first_profile_loaded = true; - chip->profile_loaded = true; - chip->battery_missing = is_battery_missing(chip); - update_chg_iterm(chip); - update_cc_cv_setpoint(chip); - rc = populate_system_data(chip); - if (rc) { - pr_err("failed to read ocv properties=%d\n", rc); - return rc; - } - estimate_battery_age(chip, &chip->actual_cap_uah); - schedule_work(&chip->status_change_work); - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - fg_relax(&chip->profile_wakeup_source); - pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip), - fg_data[FG_DATA_VOLTAGE].value); - return rc; -no_profile: - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - fg_relax(&chip->profile_wakeup_source); - return rc; -reschedule: - schedule_delayed_work( - &chip->batt_profile_init, - msecs_to_jiffies(BATTERY_PSY_WAIT_MS)); - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(0)); - fg_relax(&chip->profile_wakeup_source); - return 0; -} - -static void check_empty_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - check_empty_work.work); - - if (fg_is_batt_empty(chip)) { - if (fg_debug_mask & FG_STATUS) - pr_info("EMPTY SOC high\n"); - chip->soc_empty = true; - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - } - fg_relax(&chip->empty_check_wakeup_source); -} - -static void batt_profile_init(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - batt_profile_init.work); - - if (fg_batt_profile_init(chip)) - pr_err("failed to initialize profile\n"); -} - -static void sysfs_restart_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - sysfs_restart_work); - int rc; - - rc = fg_do_restart(chip, false); - if (rc) - pr_err("fg restart failed: %d\n", rc); - mutex_lock(&chip->sysfs_restart_lock); - fg_restart = 0; - mutex_unlock(&chip->sysfs_restart_lock); -} - -#define SRAM_MONOTONIC_SOC_REG 0x574 -#define SRAM_MONOTONIC_SOC_OFFSET 2 -#define SRAM_RELEASE_TIMEOUT_MS 500 -static void charge_full_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - charge_full_work); - int rc; - u8 buffer[3]; - int bsoc; - int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value; - bool disable = false; - u8 reg; - - if (chip->status != POWER_SUPPLY_STATUS_FULL) { - if (fg_debug_mask & FG_STATUS) - pr_info("battery not full: %d\n", chip->status); - disable = true; - } - - fg_mem_lock(chip); - rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0); - if (rc) { - pr_err("Unable to read battery soc: %d\n", rc); - goto out; - } - if (buffer[2] <= resume_soc_raw) { - if (fg_debug_mask & FG_STATUS) - pr_info("bsoc = 0x%02x <= resume = 0x%02x\n", - buffer[2], resume_soc_raw); - disable = true; - } - if (!disable) - goto out; - - rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3, - SOC_FULL_OFFSET, 0); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - /* force a full soc value into the monotonic in order to display 100 */ - buffer[0] = 0xFF; - buffer[1] = 0xFF; - rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2, - SRAM_MONOTONIC_SOC_OFFSET, 0); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - if (fg_debug_mask & FG_STATUS) { - bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16; - pr_info("wrote %06x into soc full\n", bsoc); - } - fg_mem_release(chip); - /* - * wait one cycle to make sure the soc is updated before clearing - * the soc mask bit - */ - fg_mem_lock(chip); - fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0); -out: - fg_mem_release(chip); - if (disable) - chip->charge_full = false; -} - -static void update_bcl_thresholds(struct fg_chip *chip) -{ - u8 data[4]; - u8 mh_offset = 0, lm_offset = 0; - u16 address = 0; - int ret = 0; - - address = settings[FG_MEM_BCL_MH_THRESHOLD].address; - mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset; - lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset; - ret = fg_mem_read(chip, data, address, 4, 0, 1); - if (ret) - pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret); - else - pr_debug("Old BCL LM threshold:%x MH threshold:%x\n", - data[lm_offset], data[mh_offset]); - BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]); - BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]); - - ret = fg_mem_write(chip, data, address, 4, 0, 0); - if (ret) - pr_err("spmi write failed. addr:%03x, ret:%d\n", - address, ret); - else - pr_debug("New BCL LM threshold:%x MH threshold:%x\n", - data[lm_offset], data[mh_offset]); -} - -static int disable_bcl_lpm(struct fg_chip *chip) -{ - u8 data[4]; - u8 lm_offset = 0; - u16 address = 0; - int rc = 0; - - address = settings[FG_MEM_BCL_LM_THRESHOLD].address; - lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset; - rc = fg_mem_read(chip, data, address, 4, 0, 1); - if (rc) { - pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc); - return rc; - } - pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]); - - /* Put BCL always above LPM */ - BCL_MA_TO_ADC(0, data[lm_offset]); - - rc = fg_mem_write(chip, data, address, 4, 0, 0); - if (rc) - pr_err("spmi write failed. addr:%03x, rc:%d\n", - address, rc); - else - pr_debug("New BCL LM threshold:%x\n", data[lm_offset]); - - return rc; -} - -static void bcl_hi_power_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - bcl_hi_power_work); - int rc; - - if (chip->bcl_lpm_disabled) { - rc = disable_bcl_lpm(chip); - if (rc) - pr_err("failed to disable bcl low mode %d\n", - rc); - } else { - update_bcl_thresholds(chip); - } -} - -#define VOLT_UV_TO_VOLTCMP8(volt_uv) \ - ((volt_uv - 2500000) / 9766) -static int update_irq_volt_empty(struct fg_chip *chip) -{ - u8 data; - int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value; - - data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000); - - if (fg_debug_mask & FG_STATUS) - pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data); - return fg_mem_write(chip, &data, - settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1, - settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0); -} - -static int update_cutoff_voltage(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_voltage_raw; - s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value; - - converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000); - data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF; - data[1] = cpu_to_le16(converted_voltage_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n", - voltage_mv, converted_voltage_raw, data[0], data[1]); - return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address, - 2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0); -} - -static int update_iterm(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_current_raw; - s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value; - - converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000); - data[0] = cpu_to_le16(converted_current_raw) & 0xFF; - data[1] = cpu_to_le16(converted_current_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n", - current_ma, converted_current_raw, data[0], data[1]); - return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address, - 2, settings[FG_MEM_TERM_CURRENT].offset, 0); -} - -#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \ -do { \ - if (retval) \ - break; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," qpnp_dt_property, \ - &settings[type].value); \ - \ - if ((retval == -EINVAL) && optional) \ - retval = 0; \ - else if (retval) \ - pr_err("Error reading " #qpnp_dt_property \ - " property rc = %d\n", rc); \ -} while (0) - -#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val) \ -do { \ - if (retval) \ - break; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," qpnp_dt_property, \ - &store); \ - \ - if (retval == -EINVAL) { \ - retval = 0; \ - store = default_val; \ - } else if (retval) { \ - pr_err("Error reading " #qpnp_dt_property \ - " property rc = %d\n", rc); \ - } \ -} while (0) - -#define DEFAULT_EVALUATION_CURRENT_MA 1000 -static int fg_of_init(struct fg_chip *chip) -{ - int rc = 0, sense_type, len = 0; - const char *data; - struct device_node *node = chip->pdev->dev.of_node; - u32 temp[2] = {0}; - - OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1); - - if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) { - int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0; - - rc = of_property_read_u32_array(node, - "qcom,cold-hot-jeita-hysteresis", temp, 2); - if (rc) { - pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n", - rc); - return rc; - } - - chip->jeita_hysteresis_support = true; - chip->cold_hysteresis = temp[0]; - chip->hot_hysteresis = temp[1]; - hard_hot = settings[FG_MEM_HARD_HOT].value; - soft_hot = settings[FG_MEM_SOFT_HOT].value; - hard_cold = settings[FG_MEM_HARD_COLD].value; - soft_cold = settings[FG_MEM_SOFT_COLD].value; - if (((hard_hot - chip->hot_hysteresis) < soft_hot) || - ((hard_cold + chip->cold_hysteresis) > soft_cold)) { - chip->jeita_hysteresis_support = false; - pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n", - chip->hot_hysteresis, chip->cold_hysteresis); - } else { - pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n", - chip->cold_hysteresis, chip->hot_hysteresis); - } - } - - OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma", - rc, 1); - OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma", - rc, 1); - OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1); - OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1); - OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1); - data = of_get_property(chip->pdev->dev.of_node, - "qcom,thermal-coefficients", &len); - if (data && len == THERMAL_COEFF_N_BYTES) { - memcpy(chip->thermal_coefficients, data, len); - chip->use_thermal_coefficients = true; - } - OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1); - settings[FG_MEM_RESUME_SOC].value = - DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value - * FULL_SOC_RAW, FULL_CAPACITY); - OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1); - OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1); - OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1); - OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1); - OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1); - OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1); - OF_READ_PROPERTY(chip->learning_data.max_increment, - "cl-max-increment-deciperc", rc, 5); - OF_READ_PROPERTY(chip->learning_data.max_decrement, - "cl-max-decrement-deciperc", rc, 100); - OF_READ_PROPERTY(chip->learning_data.max_temp, - "cl-max-temp-decidegc", rc, 450); - OF_READ_PROPERTY(chip->learning_data.min_temp, - "cl-min-temp-decidegc", rc, 150); - OF_READ_PROPERTY(chip->learning_data.max_start_soc, - "cl-max-start-capacity", rc, 15); - OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv, - "cl-vbat-est-thr-uv", rc, 40000); - OF_READ_PROPERTY(chip->evaluation_current, - "aging-eval-current-ma", rc, - DEFAULT_EVALUATION_CURRENT_MA); - OF_READ_PROPERTY(chip->cc_cv_threshold_mv, - "fg-cc-cv-threshold-mv", rc, 0); - if (of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-learning-on")) - chip->batt_aging_mode = FG_AGING_CC; - else if (of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-estimation-on")) - chip->batt_aging_mode = FG_AGING_ESR; - else - chip->batt_aging_mode = FG_AGING_NONE; - if (chip->batt_aging_mode == FG_AGING_CC) { - chip->learning_data.feedback_on - = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-learning-feedback"); - } - if (fg_debug_mask & FG_AGING) - pr_info("battery aging mode: %d\n", chip->batt_aging_mode); - - /* Get the use-otp-profile property */ - chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,use-otp-profile"); - chip->hold_soc_while_full - = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,hold-soc-while-full"); - - sense_type = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,ext-sense-type"); - if (rc == 0) { - if (fg_sense_type < 0) - fg_sense_type = sense_type; - - if (fg_debug_mask & FG_STATUS) { - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - pr_info("Using internal sense\n"); - else if (fg_sense_type == EXTERNAL_CURRENT_SENSE) - pr_info("Using external sense\n"); - else - pr_info("Using default sense\n"); - } - } else { - rc = 0; - } - - chip->bad_batt_detection_en = of_property_read_bool(node, - "qcom,bad-battery-detection-enable"); - - chip->sw_rbias_ctrl = of_property_read_bool(node, - "qcom,sw-rbias-control"); - - chip->cyc_ctr.en = of_property_read_bool(node, - "qcom,cycle-counter-en"); - if (chip->cyc_ctr.en) - chip->cyc_ctr.id = 1; - - chip->esr_pulse_tune_en = of_property_read_bool(node, - "qcom,esr-pulse-tuning-en"); - - return rc; -} - -static int fg_init_irqs(struct fg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - struct platform_device *pdev = chip->pdev; - - if (of_get_available_child_count(pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", - child->full_name, rc); - return rc; - } - - if ((base == chip->vbat_adc_addr) || - (base == chip->ibat_adc_addr) || - (base == chip->tp_rev_addr)) - continue; - - rc = fg_read(chip, &subtype, - base + REG_OFFSET_PERP_SUBTYPE, 1); - if (rc) { - pr_err("Peripheral subtype read failed rc=%d\n", rc); - return rc; - } - - switch (subtype) { - case FG_SOC: - chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child, - "full-soc"); - if (chip->soc_irq[FULL_SOC].irq < 0) { - pr_err("Unable to get full-soc irq\n"); - return rc; - } - chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child, - "empty-soc"); - if (chip->soc_irq[EMPTY_SOC].irq < 0) { - pr_err("Unable to get low-soc irq\n"); - return rc; - } - chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child, - "delta-soc"); - if (chip->soc_irq[DELTA_SOC].irq < 0) { - pr_err("Unable to get delta-soc irq\n"); - return rc; - } - chip->soc_irq[FIRST_EST_DONE].irq - = of_irq_get_byname(child, "first-est-done"); - if (chip->soc_irq[FIRST_EST_DONE].irq < 0) { - pr_err("Unable to get first-est-done irq\n"); - return rc; - } - - rc = devm_request_irq(chip->dev, - chip->soc_irq[FULL_SOC].irq, - fg_soc_irq_handler, IRQF_TRIGGER_RISING, - "full-soc", chip); - if (rc < 0) { - pr_err("Can't request %d full-soc: %d\n", - chip->soc_irq[FULL_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[EMPTY_SOC].irq, - fg_empty_soc_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "empty-soc", chip); - if (rc < 0) { - pr_err("Can't request %d empty-soc: %d\n", - chip->soc_irq[EMPTY_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[DELTA_SOC].irq, - fg_soc_irq_handler, IRQF_TRIGGER_RISING, - "delta-soc", chip); - if (rc < 0) { - pr_err("Can't request %d delta-soc: %d\n", - chip->soc_irq[DELTA_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[FIRST_EST_DONE].irq, - fg_first_soc_irq_handler, IRQF_TRIGGER_RISING, - "first-est-done", chip); - if (rc < 0) { - pr_err("Can't request %d delta-soc: %d\n", - chip->soc_irq[FIRST_EST_DONE].irq, rc); - return rc; - } - - enable_irq_wake(chip->soc_irq[DELTA_SOC].irq); - enable_irq_wake(chip->soc_irq[FULL_SOC].irq); - enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); - break; - case FG_MEMIF: - chip->mem_irq[FG_MEM_AVAIL].irq - = of_irq_get_byname(child, "mem-avail"); - if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) { - pr_err("Unable to get mem-avail irq\n"); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->mem_irq[FG_MEM_AVAIL].irq, - fg_mem_avail_irq_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - "mem-avail", chip); - if (rc < 0) { - pr_err("Can't request %d mem-avail: %d\n", - chip->mem_irq[FG_MEM_AVAIL].irq, rc); - return rc; - } - break; - case FG_BATT: - chip->batt_irq[BATT_MISSING].irq - = of_irq_get_byname(child, "batt-missing"); - if (chip->batt_irq[BATT_MISSING].irq < 0) { - pr_err("Unable to get batt-missing irq\n"); - rc = -EINVAL; - return rc; - } - rc = devm_request_threaded_irq(chip->dev, - chip->batt_irq[BATT_MISSING].irq, - NULL, - fg_batt_missing_irq_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "batt-missing", chip); - if (rc < 0) { - pr_err("Can't request %d batt-missing: %d\n", - chip->batt_irq[BATT_MISSING].irq, rc); - return rc; - } - chip->batt_irq[VBATT_LOW].irq - = of_irq_get_byname(child, "vbatt-low"); - if (chip->batt_irq[VBATT_LOW].irq < 0) { - pr_err("Unable to get vbatt-low irq\n"); - rc = -EINVAL; - return rc; - } - rc = devm_request_irq(chip->dev, - chip->batt_irq[VBATT_LOW].irq, - fg_vbatt_low_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - "vbatt-low", chip); - if (rc < 0) { - pr_err("Can't request %d vbatt-low: %d\n", - chip->batt_irq[VBATT_LOW].irq, rc); - return rc; - } - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - break; - case FG_ADC: - break; - default: - pr_err("subtype %d\n", subtype); - return -EINVAL; - } - } - - return rc; -} - -static void fg_cleanup(struct fg_chip *chip) -{ - cancel_delayed_work_sync(&chip->update_sram_data); - cancel_delayed_work_sync(&chip->update_temp_work); - cancel_delayed_work_sync(&chip->update_jeita_setting); - cancel_delayed_work_sync(&chip->check_empty_work); - cancel_delayed_work_sync(&chip->batt_profile_init); - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - cancel_work_sync(&chip->rslow_comp_work); - cancel_work_sync(&chip->set_resume_soc_work); - cancel_work_sync(&chip->fg_cap_learning_work); - cancel_work_sync(&chip->dump_sram); - cancel_work_sync(&chip->status_change_work); - cancel_work_sync(&chip->cycle_count_work); - cancel_work_sync(&chip->update_esr_work); - cancel_work_sync(&chip->sysfs_restart_work); - cancel_work_sync(&chip->gain_comp_work); - cancel_work_sync(&chip->init_work); - cancel_work_sync(&chip->charge_full_work); - cancel_work_sync(&chip->esr_extract_config_work); - mutex_destroy(&chip->rslow_comp.lock); - mutex_destroy(&chip->rw_lock); - mutex_destroy(&chip->cyc_ctr.lock); - mutex_destroy(&chip->learning_data.learning_lock); - mutex_destroy(&chip->sysfs_restart_lock); - wakeup_source_trash(&chip->resume_soc_wakeup_source.source); - wakeup_source_trash(&chip->empty_check_wakeup_source.source); - wakeup_source_trash(&chip->memif_wakeup_source.source); - wakeup_source_trash(&chip->profile_wakeup_source.source); - wakeup_source_trash(&chip->update_temp_wakeup_source.source); - wakeup_source_trash(&chip->update_sram_wakeup_source.source); - wakeup_source_trash(&chip->gain_comp_wakeup_source.source); - wakeup_source_trash(&chip->capacity_learning_wakeup_source.source); - wakeup_source_trash(&chip->esr_extract_wakeup_source.source); -} - -static int fg_remove(struct platform_device *pdev) -{ - struct fg_chip *chip = dev_get_drvdata(&pdev->dev); - - fg_cleanup(chip); - dev_set_drvdata(&pdev->dev, NULL); - return 0; -} - -static int fg_memif_data_open(struct inode *inode, struct file *file) -{ - struct fg_log_buffer *log; - struct fg_trans *trans; - u8 *data_buf; - - size_t logbufsize = SZ_4K; - size_t databufsize = SZ_4K; - - if (!dbgfs_data.chip) { - pr_err("Not initialized data\n"); - return -EINVAL; - } - - /* Per file "transaction" data */ - trans = kzalloc(sizeof(*trans), GFP_KERNEL); - if (!trans) { - pr_err("Unable to allocate memory for transaction data\n"); - return -ENOMEM; - } - - /* Allocate log buffer */ - log = kzalloc(logbufsize, GFP_KERNEL); - - if (!log) { - kfree(trans); - pr_err("Unable to allocate memory for log buffer\n"); - return -ENOMEM; - } - - log->rpos = 0; - log->wpos = 0; - log->len = logbufsize - sizeof(*log); - - /* Allocate data buffer */ - data_buf = kzalloc(databufsize, GFP_KERNEL); - - if (!data_buf) { - kfree(trans); - kfree(log); - pr_err("Unable to allocate memory for data buffer\n"); - return -ENOMEM; - } - - trans->log = log; - trans->data = data_buf; - trans->cnt = dbgfs_data.cnt; - trans->addr = dbgfs_data.addr; - trans->chip = dbgfs_data.chip; - trans->offset = trans->addr; - mutex_init(&trans->memif_dfs_lock); - - file->private_data = trans; - return 0; -} - -static int fg_memif_dfs_close(struct inode *inode, struct file *file) -{ - struct fg_trans *trans = file->private_data; - - if (trans && trans->log && trans->data) { - file->private_data = NULL; - mutex_destroy(&trans->memif_dfs_lock); - kfree(trans->log); - kfree(trans->data); - kfree(trans); - } - - return 0; -} - -/** - * print_to_log: format a string and place into the log buffer - * @log: The log buffer to place the result into. - * @fmt: The format string to use. - * @...: The arguments for the format string. - * - * The return value is the number of characters written to @log buffer - * not including the trailing '\0'. - */ -static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...) -{ - va_list args; - int cnt; - char *buf = &log->data[log->wpos]; - size_t size = log->len - log->wpos; - - va_start(args, fmt); - cnt = vscnprintf(buf, size, fmt, args); - va_end(args); - - log->wpos += cnt; - return cnt; -} - -/** - * write_next_line_to_log: Writes a single "line" of data into the log buffer - * @trans: Pointer to SRAM transaction data. - * @offset: SRAM address offset to start reading from. - * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read. - * - * The 'offset' is a 12-bit SRAM address. - * - * On a successful read, the pcnt is decremented by the number of data - * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have - * been read. - */ -static int -write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt) -{ - int i, j; - u8 data[ITEMS_PER_LINE]; - struct fg_log_buffer *log = trans->log; - - int cnt = 0; - int padding = offset % ITEMS_PER_LINE; - int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt); - int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read); - - /* Buffer needs enough space for an entire line */ - if ((log->len - log->wpos) < MAX_LINE_LENGTH) - goto done; - - memcpy(data, trans->data + (offset - trans->addr), items_to_read); - - *pcnt -= items_to_read; - - /* Each line starts with the aligned offset (12-bit address) */ - cnt = print_to_log(log, "%3.3X ", offset & 0xfff); - if (cnt == 0) - goto done; - - /* If the offset is unaligned, add padding to right justify items */ - for (i = 0; i < padding; ++i) { - cnt = print_to_log(log, "-- "); - if (cnt == 0) - goto done; - } - - /* Log the data items */ - for (j = 0; i < items_to_log; ++i, ++j) { - cnt = print_to_log(log, "%2.2X ", data[j]); - if (cnt == 0) - goto done; - } - - /* If the last character was a space, then replace it with a newline */ - if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') - log->data[log->wpos - 1] = '\n'; - -done: - return cnt; -} - -/** - * get_log_data - reads data from SRAM and saves to the log buffer - * @trans: Pointer to SRAM transaction data. - * - * Returns the number of "items" read or SPMI error code for read failures. - */ -static int get_log_data(struct fg_trans *trans) -{ - int cnt, rc; - int last_cnt; - int items_read; - int total_items_read = 0; - u32 offset = trans->offset; - size_t item_cnt = trans->cnt; - struct fg_log_buffer *log = trans->log; - - if (item_cnt == 0) - return 0; - - if (item_cnt > SZ_4K) { - pr_err("Reading too many bytes\n"); - return -EINVAL; - } - - rc = fg_mem_read(trans->chip, trans->data, - trans->addr, trans->cnt, 0, 0); - if (rc) { - pr_err("dump failed: rc = %d\n", rc); - return rc; - } - /* Reset the log buffer 'pointers' */ - log->wpos = log->rpos = 0; - - /* Keep reading data until the log is full */ - do { - last_cnt = item_cnt; - cnt = write_next_line_to_log(trans, offset, &item_cnt); - items_read = last_cnt - item_cnt; - offset += items_read; - total_items_read += items_read; - } while (cnt && item_cnt > 0); - - /* Adjust the transaction offset and count */ - trans->cnt = item_cnt; - trans->offset += total_items_read; - - return total_items_read; -} - -/** - * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a - * byte array (coded as string) - * @file: file pointer - * @buf: where to put the result - * @count: maximum space available in @buf - * @ppos: starting position - * @return number of user bytes read, or negative error value - */ -static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct fg_trans *trans = file->private_data; - struct fg_log_buffer *log = trans->log; - size_t ret; - size_t len; - - mutex_lock(&trans->memif_dfs_lock); - /* Is the the log buffer empty */ - if (log->rpos >= log->wpos) { - if (get_log_data(trans) <= 0) { - len = 0; - goto unlock_mutex; - } - } - - len = min(count, log->wpos - log->rpos); - - ret = copy_to_user(buf, &log->data[log->rpos], len); - if (ret == len) { - pr_err("error copy sram register values to user\n"); - len = -EFAULT; - goto unlock_mutex; - } - - /* 'ret' is the number of bytes not copied */ - len -= ret; - - *ppos += len; - log->rpos += len; - -unlock_mutex: - mutex_unlock(&trans->memif_dfs_lock); - return len; -} - -/** - * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM. - * @file: file pointer - * @buf: user data to be written. - * @count: maximum space available in @buf - * @ppos: starting position - * @return number of user byte written, or negative error value - */ -static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int bytes_read; - int data; - int pos = 0; - int cnt = 0; - u8 *values; - size_t ret = 0; - char *kbuf; - u32 offset; - - struct fg_trans *trans = file->private_data; - - mutex_lock(&trans->memif_dfs_lock); - offset = trans->offset; - - /* Make a copy of the user data */ - kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto unlock_mutex; - } - - ret = copy_from_user(kbuf, buf, count); - if (ret == count) { - pr_err("failed to copy data from user\n"); - ret = -EFAULT; - goto free_buf; - } - - count -= ret; - *ppos += count; - kbuf[count] = '\0'; - - /* Override the text buffer with the raw data */ - values = kbuf; - - /* Parse the data in the buffer. It should be a string of numbers */ - while ((pos < count) && - sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) { - /* - * We shouldn't be receiving a string of characters that - * exceeds a size of 5 to keep this functionally correct. - * Also, we should make sure that pos never gets overflowed - * beyond the limit. - */ - if (bytes_read > 5 || bytes_read > INT_MAX - pos) { - cnt = 0; - ret = -EINVAL; - break; - } - pos += bytes_read; - values[cnt++] = data & 0xff; - } - - if (!cnt) - goto free_buf; - - pr_info("address %x, count %d\n", offset, cnt); - /* Perform the write(s) */ - - ret = fg_mem_write(trans->chip, values, offset, - cnt, 0, 0); - if (ret) { - pr_err("SPMI write failed, err = %zu\n", ret); - } else { - ret = count; - trans->offset += cnt > 4 ? 4 : cnt; - } - -free_buf: - kfree(kbuf); -unlock_mutex: - mutex_unlock(&trans->memif_dfs_lock); - return ret; -} - -static const struct file_operations fg_memif_dfs_reg_fops = { - .open = fg_memif_data_open, - .release = fg_memif_dfs_close, - .read = fg_memif_dfs_reg_read, - .write = fg_memif_dfs_reg_write, -}; - -/** - * fg_dfs_create_fs: create debugfs file system. - * @return pointer to root directory or NULL if failed to create fs - */ -static struct dentry *fg_dfs_create_fs(void) -{ - struct dentry *root, *file; - - pr_debug("Creating FG_MEM debugfs file-system\n"); - root = debugfs_create_dir(DFS_ROOT_NAME, NULL); - if (IS_ERR_OR_NULL(root)) { - pr_err("Error creating top level directory err:%ld", - (long)root); - if (PTR_ERR(root) == -ENODEV) - pr_err("debugfs is not enabled in the kernel"); - return NULL; - } - - dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data); - - file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg); - if (!file) { - pr_err("error creating help entry\n"); - goto err_remove_fs; - } - return root; - -err_remove_fs: - debugfs_remove_recursive(root); - return NULL; -} - -/** - * fg_dfs_get_root: return a pointer to FG debugfs root directory. - * @return a pointer to the existing directory, or if no root - * directory exists then create one. Directory is created with file that - * configures SRAM transaction, namely: address, and count. - * @returns valid pointer on success or NULL - */ -struct dentry *fg_dfs_get_root(void) -{ - if (dbgfs_data.root) - return dbgfs_data.root; - - if (mutex_lock_interruptible(&dbgfs_data.lock) < 0) - return NULL; - /* critical section */ - if (!dbgfs_data.root) { /* double checking idiom */ - dbgfs_data.root = fg_dfs_create_fs(); - } - mutex_unlock(&dbgfs_data.lock); - return dbgfs_data.root; -} - -/* - * fg_dfs_create: adds new fg_mem if debugfs entry - * @return zero on success - */ -int fg_dfs_create(struct fg_chip *chip) -{ - struct dentry *root; - struct dentry *file; - - root = fg_dfs_get_root(); - if (!root) - return -ENOENT; - - dbgfs_data.chip = chip; - - file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt)); - if (!file) { - pr_err("error creating 'count' entry\n"); - goto err_remove_fs; - } - - file = debugfs_create_x32("address", DFS_MODE, - root, &(dbgfs_data.addr)); - if (!file) { - pr_err("error creating 'address' entry\n"); - goto err_remove_fs; - } - - file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data, - &fg_memif_dfs_reg_fops); - if (!file) { - pr_err("error creating 'data' entry\n"); - goto err_remove_fs; - } - - return 0; - -err_remove_fs: - debugfs_remove_recursive(root); - return -ENOMEM; -} - -#define EXTERNAL_SENSE_OFFSET_REG 0x41C -#define EXT_OFFSET_TRIM_REG 0xF8 -#define SEC_ACCESS_REG 0xD0 -#define SEC_ACCESS_UNLOCK 0xA5 -#define BCL_TRIM_REV_FIXED 12 -static int bcl_trim_workaround(struct fg_chip *chip) -{ - u8 reg, rc; - - if (chip->tp_rev_addr == 0) - return 0; - - rc = fg_read(chip, ®, chip->tp_rev_addr, 1); - if (rc) { - pr_err("Failed to read tp reg, rc = %d\n", rc); - return rc; - } - if (reg >= BCL_TRIM_REV_FIXED) { - if (fg_debug_mask & FG_STATUS) - pr_info("workaround not applied, tp_rev = %d\n", reg); - return 0; - } - - rc = fg_mem_read(chip, ®, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0); - if (rc) { - pr_err("Failed to read ext sense offset trim, rc = %d\n", rc); - return rc; - } - rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG, - SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1); - - rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG, - 0xFF, reg, 1); - if (rc) { - pr_err("Failed to write ext sense offset trim, rc = %d\n", rc); - return rc; - } - - return 0; -} - -#define FG_ALG_SYSCTL_1 0x4B0 -#define SOC_CNFG 0x450 -#define SOC_DELTA_OFFSET 3 -#define DELTA_SOC_PERCENT 1 -#define I_TERM_QUAL_BIT BIT(1) -#define PATCH_NEG_CURRENT_BIT BIT(3) -#define KI_COEFF_PRED_FULL_ADDR 0x408 -#define KI_COEFF_PRED_FULL_4_0_MSB 0x88 -#define KI_COEFF_PRED_FULL_4_0_LSB 0x00 -#define TEMP_FRAC_SHIFT_REG 0x4A4 -#define FG_ADC_CONFIG_REG 0x4B8 -#define FG_BCL_CONFIG_OFFSET 0x3 -#define BCL_FORCED_HPM_IN_CHARGE BIT(2) -static int fg_common_hw_init(struct fg_chip *chip) -{ - int rc; - int resume_soc_raw; - u8 val; - - update_iterm(chip); - update_cutoff_voltage(chip); - update_irq_volt_empty(chip); - update_bcl_thresholds(chip); - - resume_soc_raw = settings[FG_MEM_RESUME_SOC].value; - if (resume_soc_raw > 0) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - return rc; - } - } else { - pr_info("FG auto recharge threshold not specified in DT\n"); - } - - if (fg_sense_type >= 0) { - rc = set_prop_sense_type(chip, fg_sense_type); - if (rc) { - pr_err("failed to config sense type %d rc=%d\n", - fg_sense_type, rc); - return rc; - } - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF, - soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value), - settings[FG_MEM_DELTA_SOC].offset); - if (rc) { - pr_err("failed to write delta soc rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF, - batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value), - settings[FG_MEM_BATT_LOW].offset); - if (rc) { - pr_err("failed to write Vbatt_low rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address, - THERM_DELAY_MASK, - therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value), - settings[FG_MEM_THERM_DELAY].offset); - if (rc) { - pr_err("failed to write therm_delay rc=%d\n", rc); - return rc; - } - - if (chip->use_thermal_coefficients) { - fg_mem_write(chip, chip->thermal_coefficients, - THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES, - THERMAL_COEFF_OFFSET, 0); - } - - if (!chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - TEMP_SENSE_ALWAYS_BIT, - BATT_TEMP_OFFSET); - if (rc) { - pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc); - return rc; - } - } - - /* Read the cycle counter back from FG SRAM */ - if (chip->cyc_ctr.en) - restore_cycle_counter(chip); - - if (chip->esr_pulse_tune_en) { - rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET, - 0); - if (rc) { - pr_err("unable to read sys_cfg_1: %d\n", rc); - return rc; - } - - if (!(val & ENABLE_ESR_PULSE_VAL)) - chip->esr_extract_disabled = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract is %sabled\n", - chip->esr_extract_disabled ? "dis" : "en"); - - rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS1_OFFSET, 0); - if (rc) { - pr_err("unable to read cbits_input_filter_reg: %d\n", - rc); - return rc; - } - - if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT)) - chip->imptr_pulse_slow_en = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled\n", - chip->imptr_pulse_slow_en ? "en" : "dis"); - - rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, - 0); - if (rc) { - pr_err("unable to read rslow cfg: %d\n", rc); - return rc; - } - - if (val & RSLOW_CFG_ON_VAL) - chip->rslow_comp.active = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("rslow_comp active is %sabled\n", - chip->rslow_comp.active ? "en" : "dis"); - } - - return 0; -} - -static int fg_8994_hw_init(struct fg_chip *chip) -{ - int rc = 0; - u8 data[4]; - u64 esr_value; - - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - PATCH_NEG_CURRENT_BIT, - PATCH_NEG_CURRENT_BIT, - EXTERNAL_SENSE_OFFSET); - if (rc) { - pr_err("failed to write patch current bit rc=%d\n", rc); - return rc; - } - - rc = bcl_trim_workaround(chip); - if (rc) { - pr_err("failed to redo bcl trim rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) { - pr_err("failed to force hpm in charge rc=%d\n", rc); - return rc; - } - - fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0); - - data[0] = 0xA2; - data[1] = 0x12; - - rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0); - if (rc) { - pr_err("failed to write temp ocv constants rc=%d\n", rc); - return rc; - } - - data[0] = KI_COEFF_PRED_FULL_4_0_LSB; - data[1] = KI_COEFF_PRED_FULL_4_0_MSB; - fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0); - - esr_value = ESR_DEFAULT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write default ESR value rc=%d\n", rc); - else - pr_debug("set default value to esr filter\n"); - - return 0; -} - -#define FG_USBID_CONFIG_OFFSET 0x2 -#define DISABLE_USBID_DETECT_BIT BIT(0) -static int fg_8996_hw_init(struct fg_chip *chip) -{ - int rc; - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) { - pr_err("failed to force hpm in charge rc=%d\n", rc); - return rc; - } - - /* enable usbid conversions for PMi8996 V1.0 */ - if (chip->pmic_revision[REVID_DIG_MAJOR] == 1 - && chip->pmic_revision[REVID_ANA_MAJOR] == 0) { - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - DISABLE_USBID_DETECT_BIT, - 0, FG_USBID_CONFIG_OFFSET); - if (rc) { - pr_err("failed to enable usbid conversions: %d\n", rc); - return rc; - } - } - - return rc; -} - -static int fg_8950_hw_init(struct fg_chip *chip) -{ - int rc; - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) - pr_err("failed to force hpm in charge rc=%d\n", rc); - - return rc; -} - -static int fg_hw_init(struct fg_chip *chip) -{ - int rc = 0; - - rc = fg_common_hw_init(chip); - if (rc) { - pr_err("Unable to initialize FG HW rc=%d\n", rc); - return rc; - } - - /* add PMIC specific hw init */ - switch (chip->pmic_subtype) { - case PMI8994: - rc = fg_8994_hw_init(chip); - chip->wa_flag |= PULSE_REQUEST_WA; - break; - case PMI8996: - rc = fg_8996_hw_init(chip); - /* Setup workaround flag based on PMIC type */ - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - chip->wa_flag |= IADC_GAIN_COMP_WA; - if (chip->pmic_revision[REVID_DIG_MAJOR] > 1) - chip->wa_flag |= USE_CC_SOC_REG; - - break; - case PMI8950: - case PMI8937: - rc = fg_8950_hw_init(chip); - /* Setup workaround flag based on PMIC type */ - chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA; - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - chip->wa_flag |= IADC_GAIN_COMP_WA; - if (chip->pmic_revision[REVID_DIG_MAJOR] > 1) - chip->wa_flag |= USE_CC_SOC_REG; - - break; - } - if (rc) - pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc); - - pr_debug("wa_flag=0x%x\n", chip->wa_flag); - - return rc; -} - -#define DIG_MINOR 0x0 -#define DIG_MAJOR 0x1 -#define ANA_MINOR 0x2 -#define ANA_MAJOR 0x3 -#define IACS_INTR_SRC_SLCT BIT(3) -static int fg_setup_memif_offset(struct fg_chip *chip) -{ - int rc; - - rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4); - if (rc) { - pr_err("Unable to read FG revision rc=%d\n", rc); - return rc; - } - - switch (chip->revision[DIG_MAJOR]) { - case DIG_REV_1: - case DIG_REV_2: - chip->offset = offset[0].address; - break; - case DIG_REV_3: - chip->offset = offset[1].address; - chip->ima_supported = true; - break; - default: - pr_err("Digital Major rev=%d not supported\n", - chip->revision[DIG_MAJOR]); - return -EINVAL; - } - - if (chip->ima_supported) { - /* - * Change the FG_MEM_INT interrupt to track IACS_READY - * condition instead of end-of-transaction. This makes sure - * that the next transaction starts only after the hw is ready. - */ - rc = fg_masked_write(chip, - chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT, - IACS_INTR_SRC_SLCT, 1); - if (rc) { - pr_err("failed to configure interrupt source %d\n", rc); - return rc; - } - } - - return 0; -} - -static int fg_detect_pmic_type(struct fg_chip *chip) -{ - struct pmic_revid_data *pmic_rev_id; - struct device_node *revid_dev_node; - - revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(pmic_rev_id)) { - pr_err("Unable to get pmic_revid rc=%ld\n", - PTR_ERR(pmic_rev_id)); - /* - * the revid peripheral must be registered, any failure - * here only indicates that the rev-id module has not - * probed yet. - */ - return -EPROBE_DEFER; - } - - switch (pmic_rev_id->pmic_subtype) { - case PMI8994: - case PMI8950: - case PMI8937: - case PMI8996: - chip->pmic_subtype = pmic_rev_id->pmic_subtype; - chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1; - chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2; - chip->pmic_revision[REVID_ANA_MAJOR] = pmic_rev_id->rev3; - chip->pmic_revision[REVID_DIG_MAJOR] = pmic_rev_id->rev4; - break; - default: - pr_err("PMIC subtype %d not supported\n", - pmic_rev_id->pmic_subtype); - return -EINVAL; - } - - return 0; -} - -#define INIT_JEITA_DELAY_MS 1000 - -static void delayed_init_work(struct work_struct *work) -{ - u8 reg[2]; - int rc; - struct fg_chip *chip = container_of(work, - struct fg_chip, - init_work); - - /* hold memory access until initialization finishes */ - fg_mem_lock(chip); - - rc = fg_hw_init(chip); - if (rc) { - pr_err("failed to hw init rc = %d\n", rc); - fg_mem_release(chip); - fg_cleanup(chip); - return; - } - /* release memory access before update_sram_data is called */ - fg_mem_release(chip); - - schedule_delayed_work( - &chip->update_jeita_setting, - msecs_to_jiffies(INIT_JEITA_DELAY_MS)); - - if (chip->last_sram_update_time == 0) - update_sram_data_work(&chip->update_sram_data.work); - - if (chip->last_temp_update_time == 0) - update_temp_data(&chip->update_temp_work.work); - - if (!chip->use_otp_profile) - schedule_delayed_work(&chip->batt_profile_init, 0); - - if (chip->wa_flag & IADC_GAIN_COMP_WA) { - /* read default gain config */ - rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to read default gain rc=%d\n", rc); - goto done; - } - - if (reg[1] || reg[0]) { - /* - * Default gain register has valid value: - * - write to gain register. - */ - rc = fg_mem_write(chip, reg, GAIN_REG, 2, - GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write gain rc=%d\n", rc); - goto done; - } - } else { - /* - * Default gain register is invalid: - * - read gain register for default gain value - * - write to default gain register. - */ - rc = fg_mem_read(chip, reg, GAIN_REG, 2, - GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to read gain rc=%d\n", rc); - goto done; - } - rc = fg_mem_write(chip, reg, K_VCOR_REG, 2, - DEF_GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write default gain rc=%d\n", - rc); - goto done; - } - } - - chip->iadc_comp_data.dfl_gain_reg[0] = reg[0]; - chip->iadc_comp_data.dfl_gain_reg[1] = reg[1]; - chip->iadc_comp_data.dfl_gain = half_float(reg); - chip->input_present = is_input_present(chip); - chip->otg_present = is_otg_present(chip); - chip->init_done = true; - - pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n", - reg[1], reg[0], chip->iadc_comp_data.dfl_gain); - } - - pr_debug("FG: HW_init success\n"); - - return; -done: - fg_cleanup(chip); -} - -static int fg_probe(struct platform_device *pdev) -{ - struct device *dev = &(pdev->dev); - struct fg_chip *chip; - struct device_node *child; - unsigned int base; - u8 subtype, reg; - int rc = 0; - struct power_supply_config bms_psy_cfg; - - if (!pdev) { - pr_err("no valid spmi pointer\n"); - return -ENODEV; - } - - if (!pdev->dev.of_node) { - pr_err("device node missing\n"); - return -ENODEV; - } - - chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL); - if (chip == NULL) { - pr_err("Can't allocate fg_chip\n"); - return -ENOMEM; - } - chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!chip->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); - return -EINVAL; - } - - chip->pdev = pdev; - chip->dev = &(pdev->dev); - - wakeup_source_init(&chip->empty_check_wakeup_source.source, - "qpnp_fg_empty_check"); - wakeup_source_init(&chip->memif_wakeup_source.source, - "qpnp_fg_memaccess"); - wakeup_source_init(&chip->profile_wakeup_source.source, - "qpnp_fg_profile"); - wakeup_source_init(&chip->update_temp_wakeup_source.source, - "qpnp_fg_update_temp"); - wakeup_source_init(&chip->update_sram_wakeup_source.source, - "qpnp_fg_update_sram"); - wakeup_source_init(&chip->resume_soc_wakeup_source.source, - "qpnp_fg_set_resume_soc"); - wakeup_source_init(&chip->gain_comp_wakeup_source.source, - "qpnp_fg_gain_comp"); - wakeup_source_init(&chip->capacity_learning_wakeup_source.source, - "qpnp_fg_cap_learning"); - wakeup_source_init(&chip->esr_extract_wakeup_source.source, - "qpnp_fg_esr_extract"); - mutex_init(&chip->rw_lock); - mutex_init(&chip->cyc_ctr.lock); - mutex_init(&chip->learning_data.learning_lock); - mutex_init(&chip->rslow_comp.lock); - mutex_init(&chip->sysfs_restart_lock); - INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); - INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work); - INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data); - INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work); - INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init); - INIT_WORK(&chip->rslow_comp_work, rslow_comp_work); - INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work); - INIT_WORK(&chip->dump_sram, dump_sram); - INIT_WORK(&chip->status_change_work, status_change_work); - INIT_WORK(&chip->cycle_count_work, update_cycle_count); - INIT_WORK(&chip->battery_age_work, battery_age_work); - INIT_WORK(&chip->update_esr_work, update_esr_value); - INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work); - INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work); - INIT_WORK(&chip->init_work, delayed_init_work); - INIT_WORK(&chip->charge_full_work, charge_full_work); - INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work); - INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work); - INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work); - alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME, - fg_cap_learning_alarm_cb); - init_completion(&chip->sram_access_granted); - init_completion(&chip->sram_access_revoked); - complete_all(&chip->sram_access_revoked); - init_completion(&chip->batt_id_avail); - init_completion(&chip->first_soc_done); - dev_set_drvdata(&pdev->dev, chip); - - if (of_get_available_child_count(pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - rc = -ENXIO; - goto of_init_fail; - } - - for_each_available_child_of_node(pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", - child->full_name, rc); - goto of_init_fail; - } - - if (strcmp("qcom,fg-adc-vbat", child->name) == 0) { - chip->vbat_adc_addr = base; - continue; - } else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) { - chip->ibat_adc_addr = base; - continue; - } else if (strcmp("qcom,revid-tp-rev", child->name) == 0) { - chip->tp_rev_addr = base; - continue; - } - - rc = fg_read(chip, &subtype, - base + REG_OFFSET_PERP_SUBTYPE, 1); - if (rc) { - pr_err("Peripheral subtype read failed rc=%d\n", rc); - goto of_init_fail; - } - - switch (subtype) { - case FG_SOC: - chip->soc_base = base; - break; - case FG_MEMIF: - chip->mem_base = base; - break; - case FG_BATT: - chip->batt_base = base; - break; - default: - pr_err("Invalid peripheral subtype=0x%x\n", subtype); - rc = -EINVAL; - } - } - - rc = fg_detect_pmic_type(chip); - if (rc) { - pr_err("Unable to detect PMIC type rc=%d\n", rc); - return rc; - } - - rc = fg_setup_memif_offset(chip); - if (rc) { - pr_err("Unable to setup mem_if offsets rc=%d\n", rc); - goto of_init_fail; - } - - rc = fg_of_init(chip); - if (rc) { - pr_err("failed to parse devicetree rc%d\n", rc); - goto of_init_fail; - } - - if (chip->jeita_hysteresis_support) { - rc = fg_init_batt_temp_state(chip); - if (rc) { - pr_err("failed to get battery status rc%d\n", rc); - goto of_init_fail; - } - } - - /* check if the first estimate is already finished at this time */ - if (is_first_est_done(chip)) - complete_all(&chip->first_soc_done); - - reg = 0xFF; - rc = fg_write(chip, ®, INT_EN_CLR(chip->mem_base), 1); - if (rc) { - pr_err("failed to clear interrupts %d\n", rc); - goto of_init_fail; - } - - rc = fg_init_irqs(chip); - if (rc) { - pr_err("failed to request interrupts %d\n", rc); - goto cancel_work; - } - - chip->batt_type = default_batt_type; - - chip->bms_psy_d.name = "bms"; - chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS; - chip->bms_psy_d.properties = fg_power_props; - chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props); - chip->bms_psy_d.get_property = fg_power_get_property; - chip->bms_psy_d.set_property = fg_power_set_property; - chip->bms_psy_d.external_power_changed = fg_external_power_changed; - chip->bms_psy_d.property_is_writeable = fg_property_is_writeable; - - bms_psy_cfg.drv_data = chip; - bms_psy_cfg.supplied_to = fg_supplicants; - bms_psy_cfg.num_supplicants = ARRAY_SIZE(fg_supplicants); - bms_psy_cfg.of_node = NULL; - chip->bms_psy = devm_power_supply_register(chip->dev, - &chip->bms_psy_d, - &bms_psy_cfg); - if (IS_ERR(chip->bms_psy)) { - pr_err("batt failed to register rc = %ld\n", - PTR_ERR(chip->bms_psy)); - goto of_init_fail; - } - chip->power_supply_registered = true; - /* - * Just initialize the batt_psy_name here. Power supply - * will be obtained later. - */ - chip->batt_psy_name = "battery"; - - if (chip->mem_base) { - rc = fg_dfs_create(chip); - if (rc < 0) { - pr_err("failed to create debugfs rc = %d\n", rc); - goto cancel_work; - } - } - - schedule_work(&chip->init_work); - - pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n", - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR], - chip->pmic_subtype); - - return rc; - -cancel_work: - cancel_delayed_work_sync(&chip->update_jeita_setting); - cancel_delayed_work_sync(&chip->update_sram_data); - cancel_delayed_work_sync(&chip->update_temp_work); - cancel_delayed_work_sync(&chip->check_empty_work); - cancel_delayed_work_sync(&chip->batt_profile_init); - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - cancel_work_sync(&chip->set_resume_soc_work); - cancel_work_sync(&chip->fg_cap_learning_work); - cancel_work_sync(&chip->dump_sram); - cancel_work_sync(&chip->status_change_work); - cancel_work_sync(&chip->cycle_count_work); - cancel_work_sync(&chip->update_esr_work); - cancel_work_sync(&chip->rslow_comp_work); - cancel_work_sync(&chip->sysfs_restart_work); - cancel_work_sync(&chip->gain_comp_work); - cancel_work_sync(&chip->init_work); - cancel_work_sync(&chip->charge_full_work); - cancel_work_sync(&chip->bcl_hi_power_work); - cancel_work_sync(&chip->esr_extract_config_work); -of_init_fail: - mutex_destroy(&chip->rslow_comp.lock); - mutex_destroy(&chip->rw_lock); - mutex_destroy(&chip->cyc_ctr.lock); - mutex_destroy(&chip->learning_data.learning_lock); - mutex_destroy(&chip->sysfs_restart_lock); - wakeup_source_trash(&chip->resume_soc_wakeup_source.source); - wakeup_source_trash(&chip->empty_check_wakeup_source.source); - wakeup_source_trash(&chip->memif_wakeup_source.source); - wakeup_source_trash(&chip->profile_wakeup_source.source); - wakeup_source_trash(&chip->update_temp_wakeup_source.source); - wakeup_source_trash(&chip->update_sram_wakeup_source.source); - wakeup_source_trash(&chip->gain_comp_wakeup_source.source); - wakeup_source_trash(&chip->capacity_learning_wakeup_source.source); - wakeup_source_trash(&chip->esr_extract_wakeup_source.source); - return rc; -} - -static void check_and_update_sram_data(struct fg_chip *chip) -{ - unsigned long current_time = 0, next_update_time, time_left; - - get_current_time(¤t_time); - - next_update_time = chip->last_temp_update_time - + (TEMP_PERIOD_UPDATE_MS / 1000); - - if (next_update_time > current_time) - time_left = next_update_time - current_time; - else - time_left = 0; - - schedule_delayed_work( - &chip->update_temp_work, msecs_to_jiffies(time_left * 1000)); - - next_update_time = chip->last_sram_update_time - + (fg_sram_update_period_ms / 1000); - - if (next_update_time > current_time) - time_left = next_update_time - current_time; - else - time_left = 0; - - schedule_delayed_work( - &chip->update_sram_data, msecs_to_jiffies(time_left * 1000)); -} - -static int fg_suspend(struct device *dev) -{ - struct fg_chip *chip = dev_get_drvdata(dev); - - if (!chip->sw_rbias_ctrl) - return 0; - - cancel_delayed_work(&chip->update_temp_work); - cancel_delayed_work(&chip->update_sram_data); - - return 0; -} - -static int fg_resume(struct device *dev) -{ - struct fg_chip *chip = dev_get_drvdata(dev); - - if (!chip->sw_rbias_ctrl) - return 0; - - check_and_update_sram_data(chip); - return 0; -} - -static const struct dev_pm_ops qpnp_fg_pm_ops = { - .suspend = fg_suspend, - .resume = fg_resume, -}; - -static int fg_sense_type_set(const char *val, const struct kernel_param *kp) -{ - int rc; - struct power_supply *bms_psy; - struct fg_chip *chip; - int old_fg_sense_type = fg_sense_type; - - rc = param_set_int(val, kp); - if (rc) { - pr_err("Unable to set fg_sense_type: %d\n", rc); - return rc; - } - - if (fg_sense_type != 0 && fg_sense_type != 1) { - pr_err("Bad value %d\n", fg_sense_type); - fg_sense_type = old_fg_sense_type; - return -EINVAL; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("fg_sense_type set to %d\n", fg_sense_type); - - bms_psy = power_supply_get_by_name("bms"); - if (!bms_psy) { - pr_err("bms psy not found\n"); - return 0; - } - - chip = power_supply_get_drvdata(bms_psy); - rc = set_prop_sense_type(chip, fg_sense_type); - return rc; -} - -static struct kernel_param_ops fg_sense_type_ops = { - .set = fg_sense_type_set, - .get = param_get_int, -}; - -module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644); - -static int fg_restart_set(const char *val, const struct kernel_param *kp) -{ - struct power_supply *bms_psy; - struct fg_chip *chip; - - bms_psy = power_supply_get_by_name("bms"); - if (!bms_psy) { - pr_err("bms psy not found\n"); - return 0; - } - chip = power_supply_get_drvdata(bms_psy); - - mutex_lock(&chip->sysfs_restart_lock); - if (fg_restart != 0) { - mutex_unlock(&chip->sysfs_restart_lock); - return 0; - } - fg_restart = 1; - mutex_unlock(&chip->sysfs_restart_lock); - - if (fg_debug_mask & FG_STATUS) - pr_info("fuel gauge restart initiated from sysfs...\n"); - - schedule_work(&chip->sysfs_restart_work); - return 0; -} - -static struct kernel_param_ops fg_restart_ops = { - .set = fg_restart_set, - .get = param_get_int, -}; - -module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644); - -static struct platform_driver fg_driver = { - .driver = { - .name = QPNP_FG_DEV_NAME, - .of_match_table = fg_match_table, - .pm = &qpnp_fg_pm_ops, - }, - .probe = fg_probe, - .remove = fg_remove, -}; - -static int __init fg_init(void) -{ - return platform_driver_register(&fg_driver); -} - -static void __exit fg_exit(void) -{ - return platform_driver_unregister(&fg_driver); -} - -module_init(fg_init); -module_exit(fg_exit); - -MODULE_DESCRIPTION("QPNP Fuel Gauge Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME); diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c deleted file mode 100644 index 6c1e58d046e8..000000000000 --- a/drivers/power/supply/qcom/qpnp-smbcharger.c +++ /dev/null @@ -1,8472 +0,0 @@ -/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pmic-voter.h" - -/* Mask/Bit helpers */ -#define _SMB_MASK(BITS, POS) \ - ((unsigned char)(((1 << (BITS)) - 1) << (POS))) -#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \ - _SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \ - (RIGHT_BIT_POS)) -/* Config registers */ -struct smbchg_regulator { - struct regulator_desc rdesc; - struct regulator_dev *rdev; -}; - -struct parallel_usb_cfg { - struct power_supply *psy; - int min_current_thr_ma; - int min_9v_current_thr_ma; - int allowed_lowering_ma; - int current_max_ma; - bool avail; - struct mutex lock; - int initial_aicl_ma; - ktime_t last_disabled; - bool enabled_once; -}; - -struct ilim_entry { - int vmin_uv; - int vmax_uv; - int icl_pt_ma; - int icl_lv_ma; - int icl_hv_ma; -}; - -struct ilim_map { - int num; - struct ilim_entry *entries; -}; - -struct smbchg_version_tables { - const int *dc_ilim_ma_table; - int dc_ilim_ma_len; - const int *usb_ilim_ma_table; - int usb_ilim_ma_len; - const int *iterm_ma_table; - int iterm_ma_len; - const int *fcc_comp_table; - int fcc_comp_len; - const int *aicl_rerun_period_table; - int aicl_rerun_period_len; - int rchg_thr_mv; -}; - -struct smbchg_chip { - struct device *dev; - struct platform_device *pdev; - struct regmap *regmap; - int schg_version; - - /* peripheral register address bases */ - u16 chgr_base; - u16 bat_if_base; - u16 usb_chgpth_base; - u16 dc_chgpth_base; - u16 otg_base; - u16 misc_base; - - int fake_battery_soc; - u8 revision[4]; - - /* configuration parameters */ - int iterm_ma; - int usb_max_current_ma; - int typec_current_ma; - int dc_max_current_ma; - int dc_target_current_ma; - int cfg_fastchg_current_ma; - int fastchg_current_ma; - int vfloat_mv; - int fastchg_current_comp; - int float_voltage_comp; - int resume_delta_mv; - int safety_time; - int prechg_safety_time; - int bmd_pin_src; - int jeita_temp_hard_limit; - int aicl_rerun_period_s; - bool use_vfloat_adjustments; - bool iterm_disabled; - bool bmd_algo_disabled; - bool soft_vfloat_comp_disabled; - bool chg_enabled; - bool charge_unknown_battery; - bool chg_inhibit_en; - bool chg_inhibit_source_fg; - bool low_volt_dcin; - bool cfg_chg_led_support; - bool cfg_chg_led_sw_ctrl; - bool vbat_above_headroom; - bool force_aicl_rerun; - bool hvdcp3_supported; - bool restricted_charging; - bool skip_usb_suspend_for_fake_battery; - bool hvdcp_not_supported; - bool otg_pinctrl; - u8 original_usbin_allowance; - struct parallel_usb_cfg parallel; - struct delayed_work parallel_en_work; - struct dentry *debug_root; - struct smbchg_version_tables tables; - - /* wipower params */ - struct ilim_map wipower_default; - struct ilim_map wipower_pt; - struct ilim_map wipower_div2; - struct qpnp_vadc_chip *vadc_dev; - bool wipower_dyn_icl_avail; - struct ilim_entry current_ilim; - struct mutex wipower_config; - bool wipower_configured; - struct qpnp_adc_tm_btm_param param; - - /* flash current prediction */ - int rpara_uohm; - int rslow_uohm; - int vled_max_uv; - - /* vfloat adjustment */ - int max_vbat_sample; - int n_vbat_samples; - - /* status variables */ - int wake_reasons; - int previous_soc; - int usb_online; - bool dc_present; - bool usb_present; - bool batt_present; - int otg_retries; - ktime_t otg_enable_time; - bool aicl_deglitch_short; - bool safety_timer_en; - bool aicl_complete; - bool usb_ov_det; - bool otg_pulse_skip_dis; - const char *battery_type; - enum power_supply_type usb_supply_type; - bool very_weak_charger; - bool parallel_charger_detected; - bool chg_otg_enabled; - bool flash_triggered; - bool flash_active; - bool icl_disabled; - u32 wa_flags; - int usb_icl_delta; - bool typec_dfp; - unsigned int usb_current_max; - unsigned int usb_health; - - /* jeita and temperature */ - bool batt_hot; - bool batt_cold; - bool batt_warm; - bool batt_cool; - unsigned int thermal_levels; - unsigned int therm_lvl_sel; - unsigned int *thermal_mitigation; - - /* irqs */ - int batt_hot_irq; - int batt_warm_irq; - int batt_cool_irq; - int batt_cold_irq; - int batt_missing_irq; - int vbat_low_irq; - int chg_hot_irq; - int chg_term_irq; - int taper_irq; - bool taper_irq_enabled; - struct mutex taper_irq_lock; - int recharge_irq; - int fastchg_irq; - int wdog_timeout_irq; - int power_ok_irq; - int dcin_uv_irq; - int usbin_uv_irq; - int usbin_ov_irq; - int src_detect_irq; - int otg_fail_irq; - int otg_oc_irq; - int aicl_done_irq; - int usbid_change_irq; - int chg_error_irq; - bool enable_aicl_wake; - - /* psy */ - struct power_supply_desc usb_psy_d; - struct power_supply *usb_psy; - struct power_supply_desc batt_psy_d; - struct power_supply *batt_psy; - struct power_supply_desc dc_psy_d; - struct power_supply *dc_psy; - struct power_supply *bms_psy; - struct power_supply *typec_psy; - int dc_psy_type; - const char *bms_psy_name; - const char *battery_psy_name; - - struct regulator *dpdm_reg; - struct smbchg_regulator otg_vreg; - struct smbchg_regulator ext_otg_vreg; - struct work_struct usb_set_online_work; - struct delayed_work vfloat_adjust_work; - struct delayed_work hvdcp_det_work; - spinlock_t sec_access_lock; - struct mutex therm_lvl_lock; - struct mutex usb_set_online_lock; - struct mutex pm_lock; - /* aicl deglitch workaround */ - unsigned long first_aicl_seconds; - int aicl_irq_count; - struct mutex usb_status_lock; - bool hvdcp_3_det_ignore_uv; - struct completion src_det_lowered; - struct completion src_det_raised; - struct completion usbin_uv_lowered; - struct completion usbin_uv_raised; - int pulse_cnt; - struct led_classdev led_cdev; - bool skip_usb_notification; - u32 vchg_adc_channel; - struct qpnp_vadc_chip *vchg_vadc_dev; - - /* voters */ - struct votable *fcc_votable; - struct votable *usb_icl_votable; - struct votable *dc_icl_votable; - struct votable *usb_suspend_votable; - struct votable *dc_suspend_votable; - struct votable *battchg_suspend_votable; - struct votable *hw_aicl_rerun_disable_votable; - struct votable *hw_aicl_rerun_enable_indirect_votable; - struct votable *aicl_deglitch_short_votable; - - /* extcon for VBUS / ID notification to USB */ - struct extcon_dev *extcon; -}; - -enum qpnp_schg { - QPNP_SCHG, - QPNP_SCHG_LITE, -}; - -static char *version_str[] = { - [QPNP_SCHG] = "SCHG", - [QPNP_SCHG_LITE] = "SCHG_LITE", -}; - -enum pmic_subtype { - PMI8994 = 10, - PMI8950 = 17, - PMI8996 = 19, - PMI8937 = 55, -}; - -enum smbchg_wa { - SMBCHG_AICL_DEGLITCH_WA = BIT(0), - SMBCHG_HVDCP_9V_EN_WA = BIT(1), - SMBCHG_USB100_WA = BIT(2), - SMBCHG_BATT_OV_WA = BIT(3), - SMBCHG_CC_ESR_WA = BIT(4), - SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5), - SMBCHG_RESTART_WA = BIT(6), - SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7), -}; - -enum print_reason { - PR_REGISTER = BIT(0), - PR_INTERRUPT = BIT(1), - PR_STATUS = BIT(2), - PR_DUMP = BIT(3), - PR_PM = BIT(4), - PR_MISC = BIT(5), - PR_WIPOWER = BIT(6), - PR_TYPEC = BIT(7), -}; - -enum wake_reason { - PM_PARALLEL_CHECK = BIT(0), - PM_REASON_VFLOAT_ADJUST = BIT(1), - PM_ESR_PULSE = BIT(2), - PM_PARALLEL_TAPER = BIT(3), - PM_DETECT_HVDCP = BIT(4), -}; - -/* fcc_voters */ -#define ESR_PULSE_FCC_VOTER "ESR_PULSE_FCC_VOTER" -#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER" -#define RESTRICTED_CHG_FCC_VOTER "RESTRICTED_CHG_FCC_VOTER" - -/* ICL VOTERS */ -#define PSY_ICL_VOTER "PSY_ICL_VOTER" -#define THERMAL_ICL_VOTER "THERMAL_ICL_VOTER" -#define HVDCP_ICL_VOTER "HVDCP_ICL_VOTER" -#define USER_ICL_VOTER "USER_ICL_VOTER" -#define WEAK_CHARGER_ICL_VOTER "WEAK_CHARGER_ICL_VOTER" -#define SW_AICL_ICL_VOTER "SW_AICL_ICL_VOTER" -#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER" - -/* USB SUSPEND VOTERS */ -/* userspace has suspended charging altogether */ -#define USER_EN_VOTER "USER_EN_VOTER" -/* - * this specific path has been suspended through the power supply - * framework - */ -#define POWER_SUPPLY_EN_VOTER "POWER_SUPPLY_EN_VOTER" -/* - * the usb driver has suspended this path by setting a current limit - * of < 2MA - */ -#define USB_EN_VOTER "USB_EN_VOTER" -/* - * the thermal daemon can suspend a charge path when the system - * temperature levels rise - */ -#define THERMAL_EN_VOTER "THERMAL_EN_VOTER" -/* - * an external OTG supply is being used, suspend charge path so the - * charger does not accidentally try to charge from the external supply. - */ -#define OTG_EN_VOTER "OTG_EN_VOTER" -/* - * the charger is very weak, do not draw any current from it - */ -#define WEAK_CHARGER_EN_VOTER "WEAK_CHARGER_EN_VOTER" -/* - * fake battery voter, if battery id-resistance around 7.5 Kohm - */ -#define FAKE_BATTERY_EN_VOTER "FAKE_BATTERY_EN_VOTER" - -/* battchg_enable_voters */ - /* userspace has disabled battery charging */ -#define BATTCHG_USER_EN_VOTER "BATTCHG_USER_EN_VOTER" - /* battery charging disabled while loading battery profiles */ -#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER" - -/* hw_aicl_rerun_enable_indirect_voters */ -/* enabled via device tree */ -#define DEFAULT_CONFIG_HW_AICL_VOTER "DEFAULT_CONFIG_HW_AICL_VOTER" -/* Varb workaround voter */ -#define VARB_WORKAROUND_VOTER "VARB_WORKAROUND_VOTER" -/* SHUTDOWN workaround voter */ -#define SHUTDOWN_WORKAROUND_VOTER "SHUTDOWN_WORKAROUND_VOTER" - -/* hw_aicl_rerun_disable_voters */ -/* the results from enabling clients */ -#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER \ - "HW_AICL_RERUN_ENABLE_INDIRECT_VOTER" -/* Weak charger voter */ -#define WEAK_CHARGER_HW_AICL_VOTER "WEAK_CHARGER_HW_AICL_VOTER" - -/* aicl_short_deglitch_voters */ -/* Varb workaround voter */ -#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER \ - "VARB_WRKARND_SHORT_DEGLITCH_VOTER" -/* QC 2.0 */ -#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER" - -static const unsigned int smbchg_extcon_cable[] = { - EXTCON_USB, - EXTCON_USB_HOST, - EXTCON_NONE, -}; - -static int smbchg_debug_mask; -module_param_named( - debug_mask, smbchg_debug_mask, int, S_IRUSR | S_IWUSR -); - -static int smbchg_parallel_en = 1; -module_param_named( - parallel_en, smbchg_parallel_en, int, S_IRUSR | S_IWUSR -); - -static int smbchg_main_chg_fcc_percent = 50; -module_param_named( - main_chg_fcc_percent, smbchg_main_chg_fcc_percent, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_main_chg_icl_percent = 60; -module_param_named( - main_chg_icl_percent, smbchg_main_chg_icl_percent, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_hvdcp_icl_ma = 1800; -module_param_named( - default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_hvdcp3_icl_ma = 3000; -module_param_named( - default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_dcp_icl_ma = 1800; -module_param_named( - default_dcp_icl_ma, smbchg_default_dcp_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int wipower_dyn_icl_en; -module_param_named( - dynamic_icl_wipower_en, wipower_dyn_icl_en, - int, S_IRUSR | S_IWUSR -); - -static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS; -module_param_named( - wipower_dcin_interval, wipower_dcin_interval, - int, S_IRUSR | S_IWUSR -); - -#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000 -static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV; -module_param_named( - wipower_dcin_hyst_uv, wipower_dcin_hyst_uv, - int, S_IRUSR | S_IWUSR -); - -#define pr_smb(reason, fmt, ...) \ - do { \ - if (smbchg_debug_mask & (reason)) \ - pr_info(fmt, ##__VA_ARGS__); \ - else \ - pr_debug(fmt, ##__VA_ARGS__); \ - } while (0) - -#define pr_smb_rt(reason, fmt, ...) \ - do { \ - if (smbchg_debug_mask & (reason)) \ - pr_info_ratelimited(fmt, ##__VA_ARGS__); \ - else \ - pr_debug(fmt, ##__VA_ARGS__); \ - } while (0) - -static int smbchg_read(struct smbchg_chip *chip, u8 *val, - u16 addr, int count) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - - if (addr == 0) { - dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_read(chip->regmap, addr, val, count); - if (rc) { - dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, - rc); - return rc; - } - return 0; -} - -/* - * Writes a register to the specified by the base and limited by the bit mask - * - * Do not use this function for register writes if possible. Instead use the - * smbchg_masked_write function. - * - * The sec_access_lock must be held for all register writes and this function - * does not do that. If this function is used, please hold the spinlock or - * random secure access writes may fail. - */ -static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - int rc; - - rc = regmap_update_bits(chip->regmap, base, mask, val); - if (rc) { - dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n", - base, rc); - return rc; - } - - return 0; -} - -/* - * Writes a register to the specified by the base and limited by the bit mask - * - * This function holds a spin lock to ensure secure access register writes goes - * through. If the secure access unlock register is armed, any old register - * write can unarm the secure access unlock, causing the next write to fail. - * - * Note: do not use this for sec_access registers. Instead use the function - * below: smbchg_sec_masked_write - */ -static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&chip->sec_access_lock, flags); - rc = smbchg_masked_write_raw(chip, base, mask, val); - spin_unlock_irqrestore(&chip->sec_access_lock, flags); - - return rc; -} - -/* - * Unlocks sec access and writes to the register specified. - * - * This function holds a spin lock to exclude other register writes while - * the two writes are taking place. - */ -#define SEC_ACCESS_OFFSET 0xD0 -#define SEC_ACCESS_VALUE 0xA5 -#define PERIPHERAL_MASK 0xFF -static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - unsigned long flags; - int rc; - u16 peripheral_base = base & (~PERIPHERAL_MASK); - - spin_lock_irqsave(&chip->sec_access_lock, flags); - - rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET, - SEC_ACCESS_VALUE, SEC_ACCESS_VALUE); - if (rc) { - dev_err(chip->dev, "Unable to unlock sec_access: %d", rc); - goto out; - } - - rc = smbchg_masked_write_raw(chip, base, mask, val); - -out: - spin_unlock_irqrestore(&chip->sec_access_lock, flags); - return rc; -} - -static void smbchg_stay_awake(struct smbchg_chip *chip, int reason) -{ - int reasons; - - mutex_lock(&chip->pm_lock); - reasons = chip->wake_reasons | reason; - if (reasons != 0 && chip->wake_reasons == 0) { - pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n", - reasons, reason); - pm_stay_awake(chip->dev); - } - chip->wake_reasons = reasons; - mutex_unlock(&chip->pm_lock); -} - -static void smbchg_relax(struct smbchg_chip *chip, int reason) -{ - int reasons; - - mutex_lock(&chip->pm_lock); - reasons = chip->wake_reasons & (~reason); - if (reasons == 0 && chip->wake_reasons != 0) { - pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n", - reasons, reason); - pm_relax(chip->dev); - } - chip->wake_reasons = reasons; - mutex_unlock(&chip->pm_lock); -}; - -enum pwr_path_type { - UNKNOWN = 0, - PWR_PATH_BATTERY = 1, - PWR_PATH_USB = 2, - PWR_PATH_DC = 3, -}; - -#define PWR_PATH 0x08 -#define PWR_PATH_MASK 0x03 -static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + PWR_PATH, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc); - return PWR_PATH_BATTERY; - } - - return reg & PWR_PATH_MASK; -} - -#define RID_STS 0xB -#define RID_MASK 0xF -#define IDEV_STS 0x8 -#define RT_STS 0x10 -#define USBID_MSB 0xE -#define USBIN_UV_BIT BIT(0) -#define USBIN_OV_BIT BIT(1) -#define USBIN_SRC_DET_BIT BIT(2) -#define FMB_STS_MASK SMB_MASK(3, 0) -#define USBID_GND_THRESHOLD 0x495 -static bool is_otg_present_schg(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - u8 usbid_reg[2]; - u16 usbid_val; - /* - * After the falling edge of the usbid change interrupt occurs, - * there may still be some time before the ADC conversion for USB RID - * finishes in the fuel gauge. In the worst case, this could be up to - * 15 ms. - * - * Sleep for 20 ms (minimum msleep time) to wait for the conversion to - * finish and the USB RID status register to be updated before trying - * to detect OTG insertions. - */ - - msleep(20); - - /* - * There is a problem with USBID conversions on PMI8994 revisions - * 2.0.0. As a workaround, check that the cable is not - * detected as factory test before enabling OTG. - */ - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc); - return false; - } - - if ((reg & FMB_STS_MASK) != 0) { - pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg); - return false; - } - - rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc); - return false; - } - usbid_val = (usbid_reg[0] << 8) | usbid_reg[1]; - - if (usbid_val > USBID_GND_THRESHOLD) { - pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n", - usbid_val); - return false; - } - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RID_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read usb rid status rc = %d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "RID_STS = %02x\n", reg); - - return (reg & RID_MASK) == 0; -} - -#define RID_GND_DET_STS BIT(2) -static bool is_otg_present_schg_lite(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->otg_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read otg RT status rc = %d\n", rc); - return false; - } - - return !!(reg & RID_GND_DET_STS); -} - -static bool is_otg_present(struct smbchg_chip *chip) -{ - if (chip->schg_version == QPNP_SCHG_LITE) - return is_otg_present_schg_lite(chip); - - return is_otg_present_schg(chip); -} - -#define USBIN_9V BIT(5) -#define USBIN_UNREG BIT(4) -#define USBIN_LV BIT(3) -#define DCIN_9V BIT(2) -#define DCIN_UNREG BIT(1) -#define DCIN_LV BIT(0) -#define INPUT_STS 0x0D -#define DCIN_UV_BIT BIT(0) -#define DCIN_OV_BIT BIT(1) -static bool is_dc_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->dc_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc); - return false; - } - - if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT)) - return false; - - return true; -} - -static bool is_usb_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT)) - return false; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + INPUT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc); - return false; - } - - return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV)); -} - -static char *usb_type_str[] = { - "SDP", /* bit 0 */ - "OTHER", /* bit 1 */ - "DCP", /* bit 2 */ - "CDP", /* bit 3 */ - "NONE", /* bit 4 error case */ -}; - -#define N_TYPE_BITS 4 -#define TYPE_BITS_OFFSET 4 - -static int get_type(u8 type_reg) -{ - unsigned long type = type_reg; - type >>= TYPE_BITS_OFFSET; - return find_first_bit(&type, N_TYPE_BITS); -} - -/* helper to return the string of USB type */ -static inline char *get_usb_type_name(int type) -{ - return usb_type_str[type]; -} - -static enum power_supply_type usb_type_enum[] = { - POWER_SUPPLY_TYPE_USB, /* bit 0 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */ - POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */ -}; - -/* helper to return enum power_supply_type of USB type */ -static inline enum power_supply_type get_usb_supply_type(int type) -{ - return usb_type_enum[type]; -} - -static bool is_src_detect_high(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - return reg &= USBIN_SRC_DET_BIT; -} - -static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name, - enum power_supply_type *usb_supply_type) -{ - int rc, type; - u8 reg; - - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low\n"); - *usb_type_name = "Absent"; - *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; - return; - } - - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - *usb_type_name = "Other"; - *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; - return; - } - type = get_type(reg); - *usb_type_name = get_usb_type_name(type); - *usb_supply_type = get_usb_supply_type(type); -} - -#define CHGR_STS 0x0E -#define BATT_LESS_THAN_2V BIT(4) -#define CHG_HOLD_OFF_BIT BIT(3) -#define CHG_TYPE_MASK SMB_MASK(2, 1) -#define CHG_TYPE_SHIFT 1 -#define BATT_NOT_CHG_VAL 0x0 -#define BATT_PRE_CHG_VAL 0x1 -#define BATT_FAST_CHG_VAL 0x2 -#define BATT_TAPER_CHG_VAL 0x3 -#define CHG_INHIBIT_BIT BIT(1) -#define BAT_TCC_REACHED_BIT BIT(7) -static int get_prop_batt_status(struct smbchg_chip *chip) -{ - int rc, status = POWER_SUPPLY_STATUS_DISCHARGING; - u8 reg = 0, chg_type; - bool charger_present, chg_inhibit; - - charger_present = is_usb_present(chip) | is_dc_present(chip) | - chip->hvdcp_3_det_ignore_uv; - if (!charger_present) - return POWER_SUPPLY_STATUS_DISCHARGING; - - rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc); - return POWER_SUPPLY_STATUS_UNKNOWN; - } - - if (reg & BAT_TCC_REACHED_BIT) - return POWER_SUPPLY_STATUS_FULL; - - chg_inhibit = reg & CHG_INHIBIT_BIT; - if (chg_inhibit) - return POWER_SUPPLY_STATUS_FULL; - - rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return POWER_SUPPLY_STATUS_UNKNOWN; - } - - if (reg & CHG_HOLD_OFF_BIT) { - /* - * when chg hold off happens the battery is - * not charging - */ - status = POWER_SUPPLY_STATUS_NOT_CHARGING; - goto out; - } - - chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; - - if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv) - status = POWER_SUPPLY_STATUS_DISCHARGING; - else - status = POWER_SUPPLY_STATUS_CHARGING; -out: - pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg); - return status; -} - -#define BAT_PRES_STATUS 0x08 -#define BAT_PRES_BIT BIT(7) -static int get_prop_batt_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->bat_if_base + BAT_PRES_STATUS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return 0; - } - - return !!(reg & BAT_PRES_BIT); -} - -static int get_prop_charge_type(struct smbchg_chip *chip) -{ - int rc; - u8 reg, chg_type; - - rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return 0; - } - - chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; - if (chg_type == BATT_NOT_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_NONE; - else if (chg_type == BATT_TAPER_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_TAPER; - else if (chg_type == BATT_FAST_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_FAST; - else if (chg_type == BATT_PRE_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; - - return POWER_SUPPLY_CHARGE_TYPE_NONE; -} - -static int set_property_on_fg(struct smbchg_chip *chip, - enum power_supply_property prop, int val) -{ - int rc; - union power_supply_propval ret = {0, }; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (!chip->bms_psy) { - pr_smb(PR_STATUS, "no bms psy found\n"); - return -EINVAL; - } - - ret.intval = val; - rc = power_supply_set_property(chip->bms_psy, prop, &ret); - if (rc) - pr_smb(PR_STATUS, - "bms psy does not allow updating prop %d rc = %d\n", - prop, rc); - - return rc; -} - -static int get_property_from_fg(struct smbchg_chip *chip, - enum power_supply_property prop, int *val) -{ - int rc; - union power_supply_propval ret = {0, }; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (!chip->bms_psy) { - pr_smb(PR_STATUS, "no bms psy found\n"); - return -EINVAL; - } - - rc = power_supply_get_property(chip->bms_psy, prop, &ret); - if (rc) { - pr_smb(PR_STATUS, - "bms psy doesn't support reading prop %d rc = %d\n", - prop, rc); - return rc; - } - - *val = ret.intval; - return rc; -} - -#define DEFAULT_BATT_CAPACITY 50 -static int get_prop_batt_capacity(struct smbchg_chip *chip) -{ - int capacity, rc; - - if (chip->fake_battery_soc >= 0) - return chip->fake_battery_soc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc); - capacity = DEFAULT_BATT_CAPACITY; - } - return capacity; -} - -#define DEFAULT_BATT_TEMP 200 -static int get_prop_batt_temp(struct smbchg_chip *chip) -{ - int temp, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc); - temp = DEFAULT_BATT_TEMP; - } - return temp; -} - -#define DEFAULT_BATT_CURRENT_NOW 0 -static int get_prop_batt_current_now(struct smbchg_chip *chip) -{ - int ua, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc); - ua = DEFAULT_BATT_CURRENT_NOW; - } - return ua; -} - -#define DEFAULT_BATT_VOLTAGE_NOW 0 -static int get_prop_batt_voltage_now(struct smbchg_chip *chip) -{ - int uv, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc); - uv = DEFAULT_BATT_VOLTAGE_NOW; - } - return uv; -} - -#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000 -static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip) -{ - int uv, rc; - - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc); - uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN; - } - return uv; -} - -static int get_prop_batt_health(struct smbchg_chip *chip) -{ - if (chip->batt_hot) - return POWER_SUPPLY_HEALTH_OVERHEAT; - else if (chip->batt_cold) - return POWER_SUPPLY_HEALTH_COLD; - else if (chip->batt_warm) - return POWER_SUPPLY_HEALTH_WARM; - else if (chip->batt_cool) - return POWER_SUPPLY_HEALTH_COOL; - else - return POWER_SUPPLY_HEALTH_GOOD; -} - -static void get_property_from_typec(struct smbchg_chip *chip, - enum power_supply_property property, - union power_supply_propval *prop) -{ - int rc; - - rc = power_supply_get_property(chip->typec_psy, - property, prop); - if (rc) - pr_smb(PR_TYPEC, - "typec psy doesn't support reading prop %d rc = %d\n", - property, rc); -} - -static void update_typec_status(struct smbchg_chip *chip) -{ - union power_supply_propval type = {0, }; - union power_supply_propval capability = {0, }; - - get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type); - if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) { - get_property_from_typec(chip, - POWER_SUPPLY_PROP_CURRENT_CAPABILITY, - &capability); - chip->typec_current_ma = capability.intval; - pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n", - type.intval, capability.intval); - } else { - pr_smb(PR_TYPEC, - "typec detection not completed continuing with USB update\n"); - } -} - -/* - * finds the index of the closest value in the array. If there are two that - * are equally close, the lower index will be returned - */ -static int find_closest_in_array(const int *arr, int len, int val) -{ - int i, closest = 0; - - if (len == 0) - return closest; - for (i = 0; i < len; i++) - if (abs(val - arr[i]) < abs(val - arr[closest])) - closest = i; - - return closest; -} - -/* finds the index of the closest smaller value in the array. */ -static int find_smaller_in_array(const int *table, int val, int len) -{ - int i; - - for (i = len - 1; i >= 0; i--) { - if (val >= table[i]) - break; - } - - return i; -} - -static const int iterm_ma_table_8994[] = { - 300, - 50, - 100, - 150, - 200, - 250, - 500, - 600 -}; - -static const int iterm_ma_table_8996[] = { - 300, - 50, - 100, - 150, - 200, - 250, - 400, - 500 -}; - -static const int usb_ilim_ma_table_8994[] = { - 300, - 400, - 450, - 475, - 500, - 550, - 600, - 650, - 700, - 900, - 950, - 1000, - 1100, - 1200, - 1400, - 1450, - 1500, - 1600, - 1800, - 1850, - 1880, - 1910, - 1930, - 1950, - 1970, - 2000, - 2050, - 2100, - 2300, - 2400, - 2500, - 3000 -}; - -static const int usb_ilim_ma_table_8996[] = { - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000, - 1100, - 1200, - 1300, - 1400, - 1450, - 1500, - 1550, - 1600, - 1700, - 1800, - 1900, - 1950, - 2000, - 2050, - 2100, - 2200, - 2300, - 2400, - 2500, - 2600, - 2700, - 2800, - 2900, - 3000 -}; - -static int dc_ilim_ma_table_8994[] = { - 300, - 400, - 450, - 475, - 500, - 550, - 600, - 650, - 700, - 900, - 950, - 1000, - 1100, - 1200, - 1400, - 1450, - 1500, - 1600, - 1800, - 1850, - 1880, - 1910, - 1930, - 1950, - 1970, - 2000, -}; - -static int dc_ilim_ma_table_8996[] = { - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000, - 1100, - 1200, - 1300, - 1400, - 1450, - 1500, - 1550, - 1600, - 1700, - 1800, - 1900, - 1950, - 2000, - 2050, - 2100, - 2200, - 2300, - 2400, -}; - -static const int fcc_comp_table_8994[] = { - 250, - 700, - 900, - 1200, -}; - -static const int fcc_comp_table_8996[] = { - 250, - 1100, - 1200, - 1500, -}; - -static const int aicl_rerun_period[] = { - 45, - 90, - 180, - 360, -}; - -static const int aicl_rerun_period_schg_lite[] = { - 3, /* 2.8s */ - 6, /* 5.6s */ - 11, /* 11.3s */ - 23, /* 22.5s */ - 45, - 90, - 180, - 360, -}; - -static void use_pmi8994_tables(struct smbchg_chip *chip) -{ - chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994; - chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994); - chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994; - chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994); - chip->tables.iterm_ma_table = iterm_ma_table_8994; - chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994); - chip->tables.fcc_comp_table = fcc_comp_table_8994; - chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994); - chip->tables.rchg_thr_mv = 200; - chip->tables.aicl_rerun_period_table = aicl_rerun_period; - chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period); -} - -static void use_pmi8996_tables(struct smbchg_chip *chip) -{ - chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996; - chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996); - chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996; - chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996); - chip->tables.iterm_ma_table = iterm_ma_table_8996; - chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996); - chip->tables.fcc_comp_table = fcc_comp_table_8996; - chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996); - chip->tables.rchg_thr_mv = 150; - chip->tables.aicl_rerun_period_table = aicl_rerun_period; - chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period); -} - -#define CMD_CHG_REG 0x42 -#define EN_BAT_CHG_BIT BIT(1) -static int smbchg_charging_en(struct smbchg_chip *chip, bool en) -{ - /* The en bit is configured active low */ - return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT); -} - -#define CMD_IL 0x40 -#define USBIN_SUSPEND_BIT BIT(4) -#define CURRENT_100_MA 100 -#define CURRENT_150_MA 150 -#define CURRENT_500_MA 500 -#define CURRENT_900_MA 900 -#define CURRENT_1500_MA 1500 -#define SUSPEND_CURRENT_MA 2 -#define ICL_OVERRIDE_BIT BIT(2) -static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend) -{ - int rc; - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc); - return rc; -} - -#define DCIN_SUSPEND_BIT BIT(3) -static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend) -{ - int rc = 0; - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc); - return rc; -} - -#define IL_CFG 0xF2 -#define DCIN_INPUT_MASK SMB_MASK(4, 0) -static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma) -{ - int i; - u8 dc_cur_val; - - i = find_smaller_in_array(chip->tables.dc_ilim_ma_table, - current_ma, chip->tables.dc_ilim_ma_len); - - if (i < 0) { - dev_err(chip->dev, "Cannot find %dma current_table\n", - current_ma); - return -EINVAL; - } - - chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i]; - dc_cur_val = i & DCIN_INPUT_MASK; - - pr_smb(PR_STATUS, "dc current set to %d mA\n", - chip->dc_max_current_ma); - return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG, - DCIN_INPUT_MASK, dc_cur_val); -} - -#define AICL_WL_SEL_CFG 0xF5 -#define AICL_WL_SEL_MASK SMB_MASK(1, 0) -#define AICL_WL_SEL_SCHG_LITE_MASK SMB_MASK(2, 0) -static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip, - int period_s) -{ - int i; - u8 reg, mask; - - i = find_smaller_in_array(chip->tables.aicl_rerun_period_table, - period_s, chip->tables.aicl_rerun_period_len); - - if (i < 0) { - dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n", - period_s); - return -EINVAL; - } - - if (chip->schg_version == QPNP_SCHG_LITE) - mask = AICL_WL_SEL_SCHG_LITE_MASK; - else - mask = AICL_WL_SEL_MASK; - - reg = i & mask; - - pr_smb(PR_STATUS, "aicl rerun period set to %ds\n", - chip->tables.aicl_rerun_period_table[i]); - return smbchg_sec_masked_write(chip, - chip->dc_chgpth_base + AICL_WL_SEL_CFG, - mask, reg); -} - -static struct power_supply *get_parallel_psy(struct smbchg_chip *chip) -{ - if (!chip->parallel.avail) - return NULL; - if (chip->parallel.psy) - return chip->parallel.psy; - chip->parallel.psy = power_supply_get_by_name("usb-parallel"); - if (!chip->parallel.psy) - pr_smb(PR_STATUS, "parallel charger not found\n"); - return chip->parallel.psy; -} - -static void smbchg_usb_update_online_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - usb_set_online_work); - bool user_enabled = !get_client_vote(chip->usb_suspend_votable, - USER_EN_VOTER); - int online; - - online = user_enabled && chip->usb_present && !chip->very_weak_charger; - - mutex_lock(&chip->usb_set_online_lock); - if (chip->usb_online != online) { - pr_smb(PR_MISC, "setting usb psy online = %d\n", online); - chip->usb_online = online; - power_supply_changed(chip->usb_psy); - } - mutex_unlock(&chip->usb_set_online_lock); -} - -#define CHGPTH_CFG 0xF4 -#define CFG_USB_2_3_SEL_BIT BIT(7) -#define CFG_USB_2 0 -#define CFG_USB_3 BIT(7) -#define USBIN_INPUT_MASK SMB_MASK(4, 0) -#define USBIN_MODE_CHG_BIT BIT(0) -#define USBIN_LIMITED_MODE 0 -#define USBIN_HC_MODE BIT(0) -#define USB51_MODE_BIT BIT(1) -#define USB51_100MA 0 -#define USB51_500MA BIT(1) -static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip, - int current_ma) -{ - int i, rc; - u8 usb_cur_val; - - if (current_ma == CURRENT_100_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc); - return rc; - } - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT, - USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT); - if (rc < 0) { - pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, - "Forcing 100mA current limit\n"); - chip->usb_max_current_ma = CURRENT_100_MA; - return rc; - } - - i = find_smaller_in_array(chip->tables.usb_ilim_ma_table, - current_ma, chip->tables.usb_ilim_ma_len); - if (i < 0) { - dev_err(chip->dev, - "Cannot find %dma current_table using %d\n", - current_ma, CURRENT_150_MA); - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) - dev_err(chip->dev, "Couldn't set %dmA rc=%d\n", - CURRENT_150_MA, rc); - else - chip->usb_max_current_ma = 150; - return rc; - } - - usb_cur_val = i & USBIN_INPUT_MASK; - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG, - USBIN_INPUT_MASK, usb_cur_val); - if (rc < 0) { - dev_err(chip->dev, "cannot write to config c rc = %d\n", rc); - return rc; - } - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT, USBIN_HC_MODE); - if (rc < 0) - dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc); - chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i]; - return rc; -} - -/* if APSD results are used - * if SDP is detected it will look at 500mA setting - * if set it will draw 500mA - * if unset it will draw 100mA - * if CDP/DCP it will look at 0x0C setting - * i.e. values in 0x41[1, 0] does not matter - */ -static int smbchg_set_usb_current_max(struct smbchg_chip *chip, - int current_ma) -{ - int rc = 0; - - /* - * if the battery is not present, do not allow the usb ICL to lower in - * order to avoid browning out the device during a hotswap. - */ - if (!chip->batt_present && current_ma < chip->usb_max_current_ma) { - pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n", - current_ma); - return 0; - } - pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma); - - if (current_ma <= SUSPEND_CURRENT_MA) { - /* suspend the usb if current <= 2mA */ - rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0); - chip->usb_max_current_ma = 0; - goto out; - } else { - rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0); - } - - switch (chip->usb_supply_type) { - case POWER_SUPPLY_TYPE_USB: - if ((current_ma < CURRENT_150_MA) && - (chip->wa_flags & SMBCHG_USB100_WA)) - current_ma = CURRENT_150_MA; - - if (current_ma < CURRENT_150_MA) { - /* force 100mA */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 100; - } - /* specific current values */ - if (current_ma == CURRENT_150_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 150; - } - if (current_ma == CURRENT_500_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_500MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 500; - } - if (current_ma == CURRENT_900_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_500MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 900; - } - break; - case POWER_SUPPLY_TYPE_USB_CDP: - if (current_ma < CURRENT_1500_MA) { - /* use override for CDP */ - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT); - if (rc < 0) - pr_err("Couldn't set override rc = %d\n", rc); - } - /* fall through */ - default: - rc = smbchg_set_high_usb_chg_current(chip, current_ma); - if (rc < 0) - pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc); - break; - } - -out: - pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n", - chip->usb_supply_type, chip->usb_max_current_ma); - return rc; -} - -#define USBIN_HVDCP_STS 0x0C -#define USBIN_HVDCP_SEL_BIT BIT(4) -#define USBIN_HVDCP_SEL_9V_BIT BIT(1) -#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT BIT(2) -#define SCHG_LITE_USBIN_HVDCP_SEL_BIT BIT(0) -static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip) -{ - int rc; - u8 reg, hvdcp_sel, hvdcp_sel_9v; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc); - return 0; - } - if (chip->schg_version == QPNP_SCHG_LITE) { - hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT; - hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT; - } else { - hvdcp_sel = USBIN_HVDCP_SEL_BIT; - hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT; - } - - if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v)) - return chip->parallel.min_9v_current_thr_ma; - return chip->parallel.min_current_thr_ma; -} - -static bool is_hvdcp_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg, hvdcp_sel; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc < 0) { - pr_err("Couldn't read hvdcp status rc = %d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg); - /* - * If a valid HVDCP is detected, notify it to the usb_psy only - * if USB is still present. - */ - if (chip->schg_version == QPNP_SCHG_LITE) - hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT; - else - hvdcp_sel = USBIN_HVDCP_SEL_BIT; - - if ((reg & hvdcp_sel) && is_usb_present(chip)) - return true; - - return false; -} - -#define FCC_CFG 0xF2 -#define FCC_500MA_VAL 0x4 -#define FCC_MASK SMB_MASK(4, 0) -static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip, - int current_ma) -{ - int i, rc; - u8 cur_val; - - /* the fcc enumerations are the same as the usb currents */ - i = find_smaller_in_array(chip->tables.usb_ilim_ma_table, - current_ma, chip->tables.usb_ilim_ma_len); - if (i < 0) { - dev_err(chip->dev, - "Cannot find %dma current_table using %d\n", - current_ma, CURRENT_500_MA); - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG, - FCC_MASK, - FCC_500MA_VAL); - if (rc < 0) - dev_err(chip->dev, "Couldn't set %dmA rc=%d\n", - CURRENT_500_MA, rc); - else - chip->fastchg_current_ma = 500; - return rc; - } - - if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) { - pr_smb(PR_STATUS, "skipping fastchg current request: %d\n", - chip->fastchg_current_ma); - return 0; - } - - cur_val = i & FCC_MASK; - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG, - FCC_MASK, cur_val); - if (rc < 0) { - dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n", - current_ma, chip->tables.usb_ilim_ma_table[cur_val]); - - chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val]; - return rc; -} - -#define ICL_STS_1_REG 0x7 -#define ICL_STS_2_REG 0x9 -#define ICL_STS_MASK 0x1F -#define AICL_SUSP_BIT BIT(6) -#define AICL_STS_BIT BIT(5) -#define USBIN_SUSPEND_STS_BIT BIT(3) -#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1) -#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0) -#define PARALLEL_REENABLE_TIMER_MS 1000 -#define PARALLEL_CHG_THRESHOLD_CURRENT 1800 -static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_2_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc); - return false; - } - - return !(reg & USBIN_SUSPEND_STS_BIT) - && (reg & USBIN_ACTIVE_PWR_SRC_BIT); -} - -static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - - if (!parallel_psy || !chip->parallel_charger_detected) - return 0; - - pval.intval = en; - return power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); -} - -#define ESR_PULSE_CURRENT_DELTA_MA 200 -static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en) -{ - int rc, fg_current_now, icl_ma; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, - &fg_current_now); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support OCV\n"); - return 0; - } - - icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA, - fg_current_now - ESR_PULSE_CURRENT_DELTA_MA); - rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma); - if (rc < 0) { - pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc); - return rc; - } - rc = smbchg_parallel_usb_charging_en(chip, !en); - return rc; -} - -#define USB_AICL_CFG 0xF3 -#define AICL_EN_BIT BIT(2) -static void smbchg_rerun_aicl(struct smbchg_chip *chip) -{ - pr_smb(PR_STATUS, "Rerunning AICL...\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - /* Add a delay so that AICL successfully clears */ - msleep(50); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); -} - -static void taper_irq_en(struct smbchg_chip *chip, bool en) -{ - mutex_lock(&chip->taper_irq_lock); - if (en != chip->taper_irq_enabled) { - if (en) { - enable_irq(chip->taper_irq); - enable_irq_wake(chip->taper_irq); - } else { - disable_irq_wake(chip->taper_irq); - disable_irq_nosync(chip->taper_irq); - } - chip->taper_irq_enabled = en; - } - mutex_unlock(&chip->taper_irq_lock); -} - -static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_1_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc); - return 0; - } - if (reg & AICL_SUSP_BIT) { - pr_warn("AICL suspended: %02x\n", reg); - return 0; - } - reg &= ICL_STS_MASK; - if (reg >= chip->tables.usb_ilim_ma_len) { - pr_warn("invalid AICL value: %02x\n", reg); - return 0; - } - return chip->tables.usb_ilim_ma_table[reg]; -} - -static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int fcc_ma, usb_icl_ma; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - pr_smb(PR_STATUS, "disabling parallel charger\n"); - chip->parallel.last_disabled = ktime_get_boottime(); - taper_irq_en(chip, false); - chip->parallel.initial_aicl_ma = 0; - chip->parallel.current_max_ma = 0; - pval.intval = SUSPEND_CURRENT_MA * 1000; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX, - &pval); - - pval.intval = false; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT, - &pval); - - fcc_ma = get_effective_result_locked(chip->fcc_votable); - usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - if (fcc_ma < 0) - pr_err("no voters for fcc, skip it\n"); - else - smbchg_set_fastchg_current_raw(chip, fcc_ma); - - if (usb_icl_ma < 0) - pr_err("no voters for usb_icl, skip it\n"); - else - smbchg_set_usb_current_max(chip, usb_icl_ma); - - smbchg_rerun_aicl(chip); -} - -#define PARALLEL_TAPER_MAX_TRIES 3 -#define PARALLEL_FCC_PERCENT_REDUCTION 75 -#define MINIMUM_PARALLEL_FCC_MA 500 -#define CHG_ERROR_BIT BIT(0) -#define BAT_TAPER_MODE_BIT BIT(6) -static void smbchg_parallel_usb_taper(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int parallel_fcc_ma, tries = 0; - u8 reg = 0; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - smbchg_stay_awake(chip, PM_PARALLEL_TAPER); -try_again: - mutex_lock(&chip->parallel.lock); - if (chip->parallel.current_max_ma == 0) { - pr_smb(PR_STATUS, "Not parallel charging, skipping\n"); - goto done; - } - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - tries += 1; - parallel_fcc_ma = pval.intval / 1000; - pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n", - tries, parallel_fcc_ma); - if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA - || tries > PARALLEL_TAPER_MAX_TRIES) { - smbchg_parallel_usb_disable(chip); - goto done; - } - pval.intval = ((parallel_fcc_ma - * PARALLEL_FCC_PERCENT_REDUCTION) / 100); - pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n", - pval.intval); - /* Change it to uA */ - pval.intval *= 1000; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - /* - * sleep here for 100 ms in order to make sure the charger has a chance - * to go back into constant current charging - */ - mutex_unlock(&chip->parallel.lock); - msleep(100); - - mutex_lock(&chip->parallel.lock); - if (chip->parallel.current_max_ma == 0) { - pr_smb(PR_STATUS, "Not parallel charging, skipping\n"); - goto done; - } - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (reg & BAT_TAPER_MODE_BIT) { - mutex_unlock(&chip->parallel.lock); - goto try_again; - } - taper_irq_en(chip, true); -done: - mutex_unlock(&chip->parallel.lock); - smbchg_relax(chip, PM_PARALLEL_TAPER); -} - -static void smbchg_parallel_usb_enable(struct smbchg_chip *chip, - int total_current_ma) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc; - int current_table_index, target_icl_ma; - int fcc_ma, main_fastchg_current_ma; - int target_parallel_fcc_ma, supplied_parallel_fcc_ma; - int parallel_chg_fcc_percent; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - pr_smb(PR_STATUS, "Attempting to enable parallel charger\n"); - pval.intval = chip->vfloat_mv + 50; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set Vflt on parallel psy rc: %d\n", rc); - return; - } - /* Set USB ICL */ - target_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - if (target_icl_ma < 0) { - pr_err("no voters for usb_icl, skip it\n"); - return; - } - new_parallel_cl_ma = total_current_ma - * (100 - smbchg_main_chg_icl_percent) / 100; - taper_irq_en(chip, true); - - pval.intval = true; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT, - &pval); - - pval.intval = new_parallel_cl_ma * 1000; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX, - &pval); - - /* read back the real amount of current we are getting */ - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &pval); - set_parallel_cl_ma = pval.intval / 1000; - chip->parallel.current_max_ma = new_parallel_cl_ma; - pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n", - new_parallel_cl_ma, set_parallel_cl_ma); - new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma); - pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n", - total_current_ma, new_pmi_cl_ma, - set_parallel_cl_ma); - smbchg_set_usb_current_max(chip, new_pmi_cl_ma); - - /* begin splitting the fast charge current */ - fcc_ma = get_effective_result_locked(chip->fcc_votable); - if (fcc_ma < 0) { - pr_err("no voters for fcc, skip it\n"); - return; - } - parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent; - target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100; - pval.intval = target_parallel_fcc_ma * 1000; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - /* check how much actual current is supplied by the parallel charger */ - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - supplied_parallel_fcc_ma = pval.intval / 1000; - pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n", - target_parallel_fcc_ma, supplied_parallel_fcc_ma); - - /* then for the main charger, use the left over FCC */ - current_table_index = find_smaller_in_array( - chip->tables.usb_ilim_ma_table, - fcc_ma - supplied_parallel_fcc_ma, - chip->tables.usb_ilim_ma_len); - main_fastchg_current_ma = - chip->tables.usb_ilim_ma_table[current_table_index]; - smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma); - pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma, - supplied_parallel_fcc_ma); - - chip->parallel.enabled_once = true; - - return; -} - -static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, - int *ret_total_current_ma) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int min_current_thr_ma, rc, type; - int total_current_ma, current_limit_ma, parallel_cl_ma; - ktime_t kt_since_last_disable; - u8 reg; - int fcc_ma = get_effective_result_locked(chip->fcc_votable); - const char *fcc_voter - = get_effective_client_locked(chip->fcc_votable); - int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - - if (!parallel_psy || !smbchg_parallel_en - || !chip->parallel_charger_detected) { - pr_smb(PR_STATUS, "Parallel charging not enabled\n"); - return false; - } - - if (fcc_ma < 0) { - pr_err("no voters for fcc! Can't enable parallel\n"); - return false; - } - if (usb_icl_ma < 0) { - pr_err("no voters for usb_icl, Can't enable parallel\n"); - return false; - } - - kt_since_last_disable = ktime_sub(ktime_get_boottime(), - chip->parallel.last_disabled); - if (chip->parallel.current_max_ma == 0 - && chip->parallel.enabled_once - && ktime_to_ms(kt_since_last_disable) - < PARALLEL_REENABLE_TIMER_MS) { - pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n", - ktime_to_ms(kt_since_last_disable)); - return false; - } - - /* - * If the battery is not present, try not to change parallel charging - * from OFF to ON or from ON to OFF, as it could cause the device to - * brown out in the instant that the USB settings are changed. - * - * Only allow parallel charging check to report false (thereby turnin - * off parallel charging) if the battery is still there, or if parallel - * charging is disabled in the first place. - */ - if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST - && (get_prop_batt_present(chip) - || chip->parallel.current_max_ma == 0)) { - pr_smb(PR_STATUS, "Not in fast charge, skipping\n"); - return false; - } - - if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) { - pr_smb(PR_STATUS, "JEITA active, skipping\n"); - return false; - } - - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - return false; - } - - type = get_type(reg); - if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) { - pr_smb(PR_STATUS, "CDP adapter, skipping\n"); - return false; - } - - if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) { - pr_smb(PR_STATUS, "SDP adapter, skipping\n"); - return false; - } - - /* - * If USBIN is suspended or not the active power source, do not enable - * parallel charging. The device may be charging off of DCIN. - */ - if (!smbchg_is_usbin_active_pwr_src(chip)) { - pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg); - return false; - } - - min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip); - if (min_current_thr_ma <= 0) { - pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n", - min_current_thr_ma); - return false; - } - - if (usb_icl_ma < min_current_thr_ma) { - pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n", - usb_icl_ma, min_current_thr_ma); - return false; - } - - if (!fcc_voter) - return false; - /* - * Suspend the parallel charger if the charging current is < 1800 mA - * and is not because of an ESR pulse. - */ - if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0) - && fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) { - pr_smb(PR_STATUS, "FCC %d lower than %d\n", - fcc_ma, - PARALLEL_CHG_THRESHOLD_CURRENT); - return false; - } - - current_limit_ma = smbchg_get_aicl_level_ma(chip); - if (current_limit_ma <= 0) - return false; - - if (chip->parallel.initial_aicl_ma == 0) { - if (current_limit_ma < min_current_thr_ma) { - pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n", - current_limit_ma, min_current_thr_ma); - return false; - } - chip->parallel.initial_aicl_ma = current_limit_ma; - } - - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &pval); - parallel_cl_ma = pval.intval / 1000; - /* - * Read back the real amount of current we are getting - * Treat 2mA as 0 because that is the suspend current setting - */ - if (parallel_cl_ma <= SUSPEND_CURRENT_MA) - parallel_cl_ma = 0; - - /* - * Set the parallel charge path's input current limit (ICL) - * to the total current / 2 - */ - total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma); - - if (total_current_ma < chip->parallel.initial_aicl_ma - - chip->parallel.allowed_lowering_ma) { - pr_smb(PR_STATUS, - "Total current reduced a lot: %d (%d + %d) < %d - %d\n", - total_current_ma, - current_limit_ma, parallel_cl_ma, - chip->parallel.initial_aicl_ma, - chip->parallel.allowed_lowering_ma); - return false; - } - - *ret_total_current_ma = total_current_ma; - return true; -} - -#define PARALLEL_CHARGER_EN_DELAY_MS 500 -static void smbchg_parallel_usb_en_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - parallel_en_work.work); - int previous_aicl_ma, total_current_ma, aicl_ma; - bool in_progress; - - /* do a check to see if the aicl is stable */ - previous_aicl_ma = smbchg_get_aicl_level_ma(chip); - msleep(PARALLEL_CHARGER_EN_DELAY_MS); - aicl_ma = smbchg_get_aicl_level_ma(chip); - if (previous_aicl_ma == aicl_ma) { - pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma); - } else { - pr_smb(PR_STATUS, - "AICL changed [%d -> %d], recheck %d ms\n", - previous_aicl_ma, aicl_ma, - PARALLEL_CHARGER_EN_DELAY_MS); - goto recheck; - } - - mutex_lock(&chip->parallel.lock); - in_progress = (chip->parallel.current_max_ma != 0); - if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) { - smbchg_parallel_usb_enable(chip, total_current_ma); - } else { - if (in_progress) { - pr_smb(PR_STATUS, "parallel charging unavailable\n"); - smbchg_parallel_usb_disable(chip); - } - } - mutex_unlock(&chip->parallel.lock); - smbchg_relax(chip, PM_PARALLEL_CHECK); - return; - -recheck: - schedule_delayed_work(&chip->parallel_en_work, 0); -} - -static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - smbchg_stay_awake(chip, PM_PARALLEL_CHECK); - schedule_delayed_work(&chip->parallel_en_work, 0); -} - -static int charging_suspend_vote_cb(struct votable *votable, void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_charging_en(chip, !suspend); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't configure batt chg: 0x%x rc = %d\n", - !suspend, rc); - } - - return rc; -} - -static int usb_suspend_vote_cb(struct votable *votable, - void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_usb_suspend(chip, suspend); - if (rc < 0) - return rc; - - if ((strcmp(client, THERMAL_EN_VOTER) == 0) - || (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0) - || (strcmp(client, USER_EN_VOTER) == 0) - || (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0)) - smbchg_parallel_usb_check_ok(chip); - - return rc; -} - -static int dc_suspend_vote_cb(struct votable *votable, - void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_dc_suspend(chip, suspend); - if (rc < 0) - return rc; - - if (chip->dc_psy_type != -EINVAL && chip->dc_psy) - power_supply_changed(chip->dc_psy); - - return rc; -} - -static int set_fastchg_current_vote_cb(struct votable *votable, - void *data, - int fcc_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - int rc; - - if (fcc_ma < 0) { - pr_err("No voters\n"); - return 0; - } - - if (chip->parallel.current_max_ma == 0) { - rc = smbchg_set_fastchg_current_raw(chip, fcc_ma); - if (rc < 0) { - pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc); - return rc; - } - } - /* - * check if parallel charging can be enabled, and if enabled, - * distribute the fcc - */ - smbchg_parallel_usb_check_ok(chip); - return 0; -} - -static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip, - int current_ma) -{ - int rc = 0; - - pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma); - - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma); - if (rc < 0) - pr_err("Couldn't vote en rc %d\n", rc); - return rc; -} - -static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip, - struct ilim_map *map, int uv) -{ - int i; - struct ilim_entry *ret = &(chip->wipower_default.entries[0]); - - for (i = 0; i < map->num; i++) { - if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv, - uv)) - ret = &map->entries[i]; - } - return ret; -} - -#define ZIN_ICL_PT 0xFC -#define ZIN_ICL_LV 0xFD -#define ZIN_ICL_HV 0xFE -#define ZIN_ICL_MASK SMB_MASK(4, 0) -static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma) -{ - int i, rc; - - i = find_smaller_in_array(chip->tables.dc_ilim_ma_table, - ma, chip->tables.dc_ilim_ma_len); - - if (i < 0) - i = 0; - - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset, - ZIN_ICL_MASK, i); - if (rc) - dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n", - offset, i, rc); - return rc; -} - -static int smbchg_wipower_ilim_config(struct smbchg_chip *chip, - struct ilim_entry *ilim) -{ - int rc = 0; - - if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_PT, ilim->icl_pt_ma, rc); - else - chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma; - } - - if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_LV, ilim->icl_lv_ma, rc); - else - chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma; - } - - if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_HV, ilim->icl_hv_ma, rc); - else - chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma; - } - return rc; -} - -static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx); -static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip, - struct ilim_entry *ilim) -{ - int rc; - - if (ilim->vmin_uv == chip->current_ilim.vmin_uv - && ilim->vmax_uv == chip->current_ilim.vmax_uv) - return 0; - - chip->param.channel = DCIN; - chip->param.btm_ctx = chip; - if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS) - wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS; - - if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S) - wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S; - - chip->param.timer_interval = wipower_dcin_interval; - chip->param.threshold_notification = &btm_notify_dcin; - chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv; - chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv; - chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE; - rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param); - if (rc) { - dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n", - rc); - } else { - chip->current_ilim.vmin_uv = ilim->vmin_uv; - chip->current_ilim.vmax_uv = ilim->vmax_uv; - pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n", - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - } - return rc; -} - -static int smbchg_wipower_icl_configure(struct smbchg_chip *chip, - int dcin_uv, bool div2) -{ - int rc = 0; - struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt; - struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv); - - rc = smbchg_wipower_ilim_config(chip, ilim); - if (rc) { - dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n", - rc, dcin_uv, div2, - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - return rc; - } - - rc = smbchg_wipower_dcin_btm_configure(chip, ilim); - if (rc) { - dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n", - rc, dcin_uv, div2, - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - return rc; - } - chip->wipower_configured = true; - return 0; -} - -static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip) -{ - int rc; - struct ilim_entry *ilim = &(chip->wipower_default.entries[0]); - - if (!chip->wipower_configured) - return; - - rc = smbchg_wipower_ilim_config(chip, ilim); - if (rc) - dev_err(chip->dev, "Couldn't config default ilim rc = %d\n", - rc); - - rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev); - if (rc) - dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n", - rc); - - chip->wipower_configured = false; - chip->current_ilim.vmin_uv = 0; - chip->current_ilim.vmax_uv = 0; - chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma; - chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma; - chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma; - pr_smb(PR_WIPOWER, "De config btm\n"); -} - -#define FV_STS 0x0C -#define DIV2_ACTIVE BIT(7) -static void __smbchg_wipower_check(struct smbchg_chip *chip) -{ - int chg_type; - bool usb_present, dc_present; - int rc; - int dcin_uv; - bool div2; - struct qpnp_vadc_result adc_result; - u8 reg; - - if (!wipower_dyn_icl_en) { - smbchg_wipower_icl_deconfigure(chip); - return; - } - - chg_type = get_prop_charge_type(chip); - usb_present = is_usb_present(chip); - dc_present = is_dc_present(chip); - if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE - && !usb_present - && dc_present - && chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) { - rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result); - if (rc) { - pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc); - return; - } - dcin_uv = adc_result.physical; - - /* check div_by_2 */ - rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS, 1); - if (rc) { - pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc); - return; - } - div2 = !!(reg & DIV2_ACTIVE); - - pr_smb(PR_WIPOWER, - "config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n", - chg_type, usb_present, dc_present, dcin_uv, - adc_result.adc_code, div2); - smbchg_wipower_icl_configure(chip, dcin_uv, div2); - } else { - pr_smb(PR_WIPOWER, - "deconfig ICL chg_type = %d usb = %d dc = %d\n", - chg_type, usb_present, dc_present); - smbchg_wipower_icl_deconfigure(chip); - } -} - -static void smbchg_wipower_check(struct smbchg_chip *chip) -{ - if (!chip->wipower_dyn_icl_avail) - return; - - mutex_lock(&chip->wipower_config); - __smbchg_wipower_check(chip); - mutex_unlock(&chip->wipower_config); -} - -static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx) -{ - struct smbchg_chip *chip = ctx; - - mutex_lock(&chip->wipower_config); - pr_smb(PR_WIPOWER, "%s state\n", - state == ADC_TM_LOW_STATE ? "low" : "high"); - chip->current_ilim.vmin_uv = 0; - chip->current_ilim.vmax_uv = 0; - __smbchg_wipower_check(chip); - mutex_unlock(&chip->wipower_config); -} - -static int force_dcin_icl_write(void *data, u64 val) -{ - struct smbchg_chip *chip = data; - - smbchg_wipower_check(chip); - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL, - force_dcin_icl_write, "0x%02llx\n"); - -/* - * set the dc charge path's maximum allowed current draw - * that may be limited by the system's thermal level - */ -static int set_dc_current_limit_vote_cb(struct votable *votable, - void *data, - int icl_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - - if (icl_ma < 0) { - pr_err("No voters\n"); - return 0; - } - - return smbchg_set_dc_current_max(chip, icl_ma); -} - -/* - * set the usb charge path's maximum allowed current draw - * that may be limited by the system's thermal level - */ -static int set_usb_current_limit_vote_cb(struct votable *votable, - void *data, - int icl_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - int rc, aicl_ma; - const char *effective_id; - - if (icl_ma < 0) { - pr_err("No voters\n"); - return 0; - } - effective_id = get_effective_client_locked(chip->usb_icl_votable); - - if (!effective_id) - return 0; - - /* disable parallel charging if HVDCP is voting for 300mA */ - if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0) - smbchg_parallel_usb_disable(chip); - - if (chip->parallel.current_max_ma == 0) { - rc = smbchg_set_usb_current_max(chip, icl_ma); - if (rc) { - pr_err("Failed to set usb current max: %d\n", rc); - return rc; - } - } - - /* skip the aicl rerun if hvdcp icl voter is active */ - if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0) - return 0; - - aicl_ma = smbchg_get_aicl_level_ma(chip); - if (icl_ma > aicl_ma) - smbchg_rerun_aicl(chip); - smbchg_parallel_usb_check_ok(chip); - return 0; -} - -static int smbchg_system_temp_level_set(struct smbchg_chip *chip, - int lvl_sel) -{ - int rc = 0; - int prev_therm_lvl; - int thermal_icl_ma; - - if (!chip->thermal_mitigation) { - dev_err(chip->dev, "Thermal mitigation not supported\n"); - return -EINVAL; - } - - if (lvl_sel < 0) { - dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel); - return -EINVAL; - } - - if (lvl_sel >= chip->thermal_levels) { - dev_err(chip->dev, "Unsupported level selected %d forcing %d\n", - lvl_sel, chip->thermal_levels - 1); - lvl_sel = chip->thermal_levels - 1; - } - - if (lvl_sel == chip->therm_lvl_sel) - return 0; - - mutex_lock(&chip->therm_lvl_lock); - prev_therm_lvl = chip->therm_lvl_sel; - chip->therm_lvl_sel = lvl_sel; - if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) { - /* - * Disable charging if highest value selected by - * setting the DC and USB path in suspend - */ - rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set dc suspend rc %d\n", rc); - goto out; - } - rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set usb suspend rc %d\n", rc); - goto out; - } - goto out; - } - - if (chip->therm_lvl_sel == 0) { - rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", - rc); - - rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't disable DC thermal ICL vote rc=%d\n", - rc); - } else { - thermal_icl_ma = - (int)chip->thermal_mitigation[chip->therm_lvl_sel]; - rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true, - thermal_icl_ma); - if (rc < 0) - pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc); - - rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true, - thermal_icl_ma); - if (rc < 0) - pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc); - } - - if (prev_therm_lvl == chip->thermal_levels - 1) { - /* - * If previously highest value was selected charging must have - * been disabed. Enable charging by taking the DC and USB path - * out of suspend. - */ - rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set dc suspend rc %d\n", rc); - goto out; - } - rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, - false, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set usb suspend rc %d\n", rc); - goto out; - } - } -out: - mutex_unlock(&chip->therm_lvl_lock); - return rc; -} - -static int smbchg_ibat_ocp_threshold_ua = 4500000; -module_param(smbchg_ibat_ocp_threshold_ua, int, 0644); - -#define UCONV 1000000LL -#define MCONV 1000LL -#define FLASH_V_THRESHOLD 3000000 -#define FLASH_VDIP_MARGIN 100000 -#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN) -#define BUCK_EFFICIENCY 800LL -static int smbchg_calc_max_flash_current(struct smbchg_chip *chip) -{ - int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc; - int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; - int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support OCV\n"); - return 0; - } - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE, - &esr_uohm); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support resistance\n"); - return 0; - } - - rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now); - if (rc) { - pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc); - return 0; - } - - rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm; - /* - * Calculate the maximum current that can pulled out of the battery - * before the battery voltage dips below a safe threshold. - */ - ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV, - rbatt_uohm); - - if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) { - /* - * If the calculated current is below the OCP threshold, then - * use it as the possible flash current. - */ - ibat_flash_ua = ibat_safe_ua - ibat_now; - vph_flash_uv = VPH_FLASH_VDIP; - } else { - /* - * If the calculated current is above the OCP threshold, then - * use the ocp threshold instead. - * - * Any higher current will be tripping the battery OCP. - */ - ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now; - vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm - * smbchg_ibat_ocp_threshold_ua, UCONV); - } - /* Calculate the input voltage of the flash module. */ - vin_flash_uv = max((chip->vled_max_uv + 500000LL), - div64_s64((vph_flash_uv * 1200), 1000)); - /* Calculate the available power for the flash module. */ - avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua; - /* - * Calculate the available amount of current the flash module can draw - * before collapsing the battery. (available power/ flash input voltage) - */ - avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV); - pr_smb(PR_MISC, - "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n", - avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm); - return (int)avail_flash_ua; -} - -#define FCC_CMP_CFG 0xF3 -#define FCC_COMP_MASK SMB_MASK(1, 0) -static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip, - int comp_current) -{ - int rc; - u8 i; - - for (i = 0; i < chip->tables.fcc_comp_len; i++) - if (comp_current == chip->tables.fcc_comp_table[i]) - break; - - if (i >= chip->tables.fcc_comp_len) - return -EINVAL; - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG, - FCC_COMP_MASK, i); - - if (rc) - dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n", - rc); - - return rc; -} - -#define CFG_TCC_REG 0xF9 -#define CHG_ITERM_MASK SMB_MASK(2, 0) -static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma) -{ - int rc; - u8 reg; - - reg = find_closest_in_array( - chip->tables.iterm_ma_table, - chip->tables.iterm_ma_len, - iterm_ma); - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CFG_TCC_REG, - CHG_ITERM_MASK, reg); - if (rc) { - dev_err(chip->dev, - "Couldn't set iterm rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n", - iterm_ma, reg); - chip->iterm_ma = iterm_ma; - - return 0; -} - -#define FV_CMP_CFG 0xF5 -#define FV_COMP_MASK SMB_MASK(5, 0) -static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code) -{ - int rc; - u8 val; - - val = code & FV_COMP_MASK; - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG, - FV_COMP_MASK, val); - - if (rc) - dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n", - rc); - - return rc; -} - -#define VFLOAT_CFG_REG 0xF4 -#define MIN_FLOAT_MV 3600 -#define MAX_FLOAT_MV 4500 -#define VFLOAT_MASK SMB_MASK(5, 0) - -#define MID_RANGE_FLOAT_MV_MIN 3600 -#define MID_RANGE_FLOAT_MIN_VAL 0x05 -#define MID_RANGE_FLOAT_STEP_MV 20 - -#define HIGH_RANGE_FLOAT_MIN_MV 4340 -#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A -#define HIGH_RANGE_FLOAT_STEP_MV 10 - -#define VHIGH_RANGE_FLOAT_MIN_MV 4360 -#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C -#define VHIGH_RANGE_FLOAT_STEP_MV 20 -static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval prop; - int rc, delta; - u8 temp; - - if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) { - dev_err(chip->dev, "bad float voltage mv =%d asked to set\n", - vfloat_mv); - return -EINVAL; - } - - if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) { - /* mid range */ - delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN; - temp = MID_RANGE_FLOAT_MIN_VAL + delta - / MID_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV; - } else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) { - /* high range */ - delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV; - temp = HIGH_RANGE_FLOAT_MIN_VAL + delta - / HIGH_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV; - } else { - /* very high range */ - delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV; - temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta - / VHIGH_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV; - } - - if (parallel_psy) { - prop.intval = vfloat_mv + 50; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); - if (rc) - dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n", - rc); - } - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG, - VFLOAT_MASK, temp); - - if (rc) - dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc); - else - chip->vfloat_mv = vfloat_mv; - - return rc; -} - -static int smbchg_float_voltage_get(struct smbchg_chip *chip) -{ - return chip->vfloat_mv; -} - -#define SFT_CFG 0xFD -#define SFT_EN_MASK SMB_MASK(5, 4) -#define SFT_TO_MASK SMB_MASK(3, 2) -#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0) -#define SFT_TIMER_DISABLE_BIT BIT(5) -#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4) -#define SAFETY_TIME_MINUTES_SHIFT 2 -static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable) -{ - int rc; - u8 reg; - - if (enable == chip->safety_timer_en) - return 0; - - if (enable) - reg = 0; - else - reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT; - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG, - SFT_EN_MASK, reg); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s safety timer rc = %d\n", - enable ? "enable" : "disable", rc); - return rc; - } - chip->safety_timer_en = enable; - return 0; -} - -enum skip_reason { - REASON_OTG_ENABLED = BIT(0), - REASON_FLASH_ENABLED = BIT(1) -}; - -#define BAT_IF_TRIM7_REG 0xF7 -#define CFG_750KHZ_BIT BIT(1) -#define MISC_CFG_NTC_VOUT_REG 0xF3 -#define CFG_NTC_VOUT_FSW_BIT BIT(0) -static int smbchg_switch_buck_frequency(struct smbchg_chip *chip, - bool flash_active) -{ - int rc; - - if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA)) - return 0; - - if (chip->flash_active == flash_active) { - pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n", - flash_active); - return 0; - } - - /* - * As per the systems team recommendation, before the flash fires, - * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the - * flash is disabled, Fsw needs to be set back to 750KHz. - */ - rc = smbchg_sec_masked_write(chip, chip->misc_base + - MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT, - flash_active ? CFG_NTC_VOUT_FSW_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n", - rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG, - CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT); - if (rc < 0) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K"); - chip->flash_active = flash_active; - return 0; -} - -#define OTG_TRIM6 0xF6 -#define TR_ENB_SKIP_BIT BIT(2) -#define OTG_EN_BIT BIT(0) -static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip, - enum skip_reason reason, bool disable) -{ - int rc; - bool disabled; - - disabled = !!chip->otg_pulse_skip_dis; - pr_smb(PR_STATUS, "%s pulse skip, reason %d\n", - disable ? "disabling" : "enabling", reason); - if (disable) - chip->otg_pulse_skip_dis |= reason; - else - chip->otg_pulse_skip_dis &= ~reason; - if (disabled == !!chip->otg_pulse_skip_dis) - return 0; - disabled = !!chip->otg_pulse_skip_dis; - - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6, - TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s otg pulse skip rc = %d\n", - disabled ? "disable" : "enable", rc); - return rc; - } - pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled"); - return 0; -} - -#define LOW_PWR_OPTIONS_REG 0xFF -#define FORCE_TLIM_BIT BIT(4) -static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable) -{ - int rc; - - rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG, - FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s otg force tlim rc = %d\n", - enable ? "enable" : "disable", rc); - return rc; - } - return rc; -} - -static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip) -{ - if (!chip->use_vfloat_adjustments) - return; - - smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST); - pr_smb(PR_STATUS, "Starting vfloat adjustments\n"); - schedule_delayed_work(&chip->vfloat_adjust_work, 0); -} - -#define FV_STS_REG 0xC -#define AICL_INPUT_STS_BIT BIT(6) -static bool smbchg_is_input_current_limited(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc); - return false; - } - - return !!(reg & AICL_INPUT_STS_BIT); -} - -#define SW_ESR_PULSE_MS 1500 -static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip) -{ - int rc, esr_count; - - if (!(chip->wa_flags & SMBCHG_CC_ESR_WA)) - return; - - if (!is_usb_present(chip) && !is_dc_present(chip)) { - pr_smb(PR_STATUS, "No inputs present, skipping\n"); - return; - } - - if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) { - pr_smb(PR_STATUS, "Not in fast charge, skipping\n"); - return; - } - - if (!smbchg_is_input_current_limited(chip)) { - pr_smb(PR_STATUS, "Not input current limited, skipping\n"); - return; - } - - set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1); - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_ESR_COUNT, &esr_count); - if (rc) { - pr_smb(PR_STATUS, - "could not read ESR counter rc = %d\n", rc); - return; - } - - /* - * The esr_count is counting down the number of fuel gauge cycles - * before a ESR pulse is needed. - * - * After a successful ESR pulse, this count is reset to some - * high number like 28. If this reaches 0, then the fuel gauge - * hardware should force a ESR pulse. - * - * However, if the device is in constant current charge mode while - * being input current limited, the ESR pulse will not affect the - * battery current, so the measurement will fail. - * - * As a failsafe, force a manual ESR pulse if this value is read as - * 0. - */ - if (esr_count != 0) { - pr_smb(PR_STATUS, "ESR count is not zero, skipping\n"); - return; - } - - pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n"); - smbchg_stay_awake(chip, PM_ESR_PULSE); - smbchg_sw_esr_pulse_en(chip, true); - msleep(SW_ESR_PULSE_MS); - pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n"); - smbchg_relax(chip, PM_ESR_PULSE); - smbchg_sw_esr_pulse_en(chip, false); -} - -static void smbchg_soc_changed(struct smbchg_chip *chip) -{ - smbchg_cc_esr_wa_check(chip); -} - -#define DC_AICL_CFG 0xF3 -#define MISC_TRIM_OPT_15_8 0xF5 -#define USB_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3)) -#define USB_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3)) -#define USB_AICL_DEGLITCH_LONG 0 -#define DC_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3)) -#define DC_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3)) -#define DC_AICL_DEGLITCH_LONG 0 -#define AICL_RERUN_MASK (BIT(5) | BIT(4)) -#define AICL_RERUN_ON (BIT(5) | BIT(4)) -#define AICL_RERUN_OFF 0 - -static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable, - void *data, - int enable, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (enable < 0) { - pr_err("No voters\n"); - enable = 0; - } - /* - * If the indirect voting result of all the clients is to enable hw aicl - * rerun, then remove our vote to disable hw aicl rerun - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0); - if (rc < 0) { - pr_err("Couldn't vote for hw rerun rc= %d\n", rc); - return rc; - } - - return rc; -} - -static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data, - int disable, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (disable < 0) { - pr_err("No voters\n"); - disable = 0; - } - - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TRIM_OPT_15_8, - AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON); - if (rc < 0) - pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc); - - return rc; -} - -static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data, - int shorter, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (shorter < 0) { - pr_err("No voters\n"); - shorter = 0; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USB_AICL_CFG, - USB_AICL_DEGLITCH_MASK, - shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG); - if (rc < 0) { - pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc); - return rc; - } - rc = smbchg_sec_masked_write(chip, - chip->dc_chgpth_base + DC_AICL_CFG, - DC_AICL_DEGLITCH_MASK, - shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG); - if (rc < 0) { - pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc); - return rc; - } - return rc; -} - -static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en) -{ - int rc; - - rc = vote(chip->aicl_deglitch_short_votable, - VARB_WORKAROUND_VOTER, en, 0); - if (rc < 0) { - pr_err("Couldn't vote %s deglitch rc=%d\n", - en ? "short" : "long", rc); - return; - } - pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long"); - - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - VARB_WORKAROUND_VOTER, en, 0); - if (rc < 0) { - pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc); - return; - } - chip->aicl_deglitch_short = en; -} - -static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip) -{ - union power_supply_propval prop = {0,}; - int rc; - bool low_volt_chgr = true; - - if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA)) - return; - - if (!is_usb_present(chip) && !is_dc_present(chip)) { - pr_smb(PR_STATUS, "Charger removed\n"); - smbchg_aicl_deglitch_wa_en(chip, false); - return; - } - - if (!chip->bms_psy) - return; - - if (is_usb_present(chip)) { - if (is_hvdcp_present(chip)) - low_volt_chgr = false; - } else if (is_dc_present(chip)) { - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) - low_volt_chgr = false; - else - low_volt_chgr = chip->low_volt_dcin; - } - - if (!low_volt_chgr) { - pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n"); - smbchg_aicl_deglitch_wa_en(chip, false); - return; - } - - /* It is possible that battery voltage went high above threshold - * when the charger is inserted and can go low because of system - * load. We shouldn't be reconfiguring AICL deglitch when this - * happens as it will lead to oscillation again which is being - * fixed here. Do it once when the battery voltage crosses the - * threshold (e.g. 4.2 V) and clear it only when the charger - * is removed. - */ - if (!chip->vbat_above_headroom) { - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop); - if (rc < 0) { - pr_err("could not read voltage_min, rc=%d\n", rc); - return; - } - chip->vbat_above_headroom = !prop.intval; - } - smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom); -} - -#define MISC_TEST_REG 0xE2 -#define BB_LOOP_DISABLE_ICL BIT(2) -static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip) -{ - bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered; - int rc = 0; - - if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA) - && icl_disabled != chip->icl_disabled) { - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TEST_REG, - BB_LOOP_DISABLE_ICL, - icl_disabled ? BB_LOOP_DISABLE_ICL : 0); - chip->icl_disabled = icl_disabled; - } - - return rc; -} - -#define UNKNOWN_BATT_TYPE "Unknown Battery" -#define LOADING_BATT_TYPE "Loading Battery Data" -static int smbchg_config_chg_battery_type(struct smbchg_chip *chip) -{ - int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0; - struct device_node *batt_node, *profile_node; - struct device_node *node = chip->pdev->dev.of_node; - union power_supply_propval prop = {0,}; - - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_BATTERY_TYPE, &prop); - if (rc) { - pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc); - return 0; - } - if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) || - !strcmp(prop.strval, LOADING_BATT_TYPE)) { - pr_smb(PR_MISC, "Battery-type not identified\n"); - return 0; - } - /* quit if there is no change in the battery-type from previous */ - if (chip->battery_type && !strcmp(prop.strval, chip->battery_type)) - return 0; - - chip->battery_type = prop.strval; - batt_node = of_parse_phandle(node, "qcom,battery-data", 0); - if (!batt_node) { - pr_smb(PR_MISC, "No batterydata available\n"); - return 0; - } - - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); - if (rc < 0) { - pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc); - return 0; - } - - profile_node = of_batterydata_get_best_profile(batt_node, - prop.intval / 1000, NULL); - if (IS_ERR_OR_NULL(profile_node)) { - rc = PTR_ERR(profile_node); - pr_err("couldn't find profile handle %d\n", rc); - return rc; - } - - /* change vfloat */ - rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", - &max_voltage_uv); - if (rc) { - pr_warn("couldn't find battery max voltage rc=%d\n", rc); - ret = rc; - } else { - if (chip->vfloat_mv != (max_voltage_uv / 1000)) { - pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n", - chip->vfloat_mv, (max_voltage_uv / 1000), - chip->battery_type); - rc = smbchg_float_voltage_set(chip, - (max_voltage_uv / 1000)); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set float voltage rc = %d\n", rc); - return rc; - } - } - } - - /* change chg term */ - rc = of_property_read_u32(profile_node, "qcom,chg-term-ua", - &iterm_ua); - if (rc && rc != -EINVAL) { - pr_warn("couldn't read battery term current=%d\n", rc); - ret = rc; - } else if (!rc) { - if (chip->iterm_ma != (iterm_ua / 1000) - && !chip->iterm_disabled) { - pr_info("Term current changed from %dmA to %dmA for battery-type %s\n", - chip->iterm_ma, (iterm_ua / 1000), - chip->battery_type); - rc = smbchg_iterm_set(chip, - (iterm_ua / 1000)); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set iterm rc = %d\n", rc); - return rc; - } - } - chip->iterm_ma = iterm_ua / 1000; - } - - /* - * Only configure from profile if fastchg-ma is not defined in the - * charger device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,fastchg-current-ma", NULL)) { - rc = of_property_read_u32(profile_node, - "qcom,fastchg-current-ma", &fastchg_ma); - if (rc) { - ret = rc; - } else { - pr_smb(PR_MISC, - "fastchg-ma changed from to %dma for battery-type %s\n", - fastchg_ma, chip->battery_type); - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, - fastchg_ma); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't vote for fastchg current rc=%d\n", - rc); - return rc; - } - } - } - - return ret; -} - -#define MAX_INV_BATT_ID 7700 -#define MIN_INV_BATT_ID 7300 -static void check_battery_type(struct smbchg_chip *chip) -{ - union power_supply_propval prop = {0,}; - bool en; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (chip->bms_psy) { - power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_BATTERY_TYPE, &prop); - en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0 - || chip->charge_unknown_battery) - && (strcmp(prop.strval, LOADING_BATT_TYPE) != 0); - vote(chip->battchg_suspend_votable, - BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0); - - if (!chip->skip_usb_suspend_for_fake_battery) { - power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); - /* suspend USB path for invalid battery-id */ - en = (prop.intval <= MAX_INV_BATT_ID && - prop.intval >= MIN_INV_BATT_ID) ? 1 : 0; - vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER, - en, 0); - } - } -} - -static void smbchg_external_power_changed(struct power_supply *psy) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - union power_supply_propval prop = {0,}; - int rc, current_limit = 0, soc; - enum power_supply_type usb_supply_type; - char *usb_type_name = "null"; - - if (chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - - smbchg_aicl_deglitch_wa_check(chip); - if (chip->bms_psy) { - check_battery_type(chip); - soc = get_prop_batt_capacity(chip); - if (chip->previous_soc != soc) { - chip->previous_soc = soc; - smbchg_soc_changed(chip); - } - - rc = smbchg_config_chg_battery_type(chip); - if (rc) - pr_smb(PR_MISC, - "Couldn't update charger configuration rc=%d\n", - rc); - } - - rc = power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); - if (rc == 0) - vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, - !prop.intval, 0); - - current_limit = chip->usb_current_max / 1000; - - /* Override if type-c charger used */ - if (chip->typec_current_ma > 500 && - current_limit < chip->typec_current_ma) - current_limit = chip->typec_current_ma; - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - - if (usb_supply_type != POWER_SUPPLY_TYPE_USB) - goto skip_current_for_non_sdp; - - pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", - usb_type_name, current_limit); - - rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, - current_limit); - if (rc < 0) - pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); - -skip_current_for_non_sdp: - smbchg_vfloat_adjust_check(chip); - - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); -} - -static int smbchg_otg_regulator_enable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - chip->otg_retries = 0; - chip->chg_otg_enabled = true; - smbchg_icl_loop_disable_check(chip); - smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true); - - /* If pin control mode then return from here */ - if (chip->otg_pinctrl) - return rc; - - /* sleep to make sure the pulse skip is actually disabled */ - msleep(20); - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, OTG_EN_BIT); - if (rc < 0) - dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc); - else - chip->otg_enable_time = ktime_get(); - pr_smb(PR_STATUS, "Enabling OTG Boost\n"); - return rc; -} - -static int smbchg_otg_regulator_disable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - if (!chip->otg_pinctrl) { - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n", - rc); - } - - chip->chg_otg_enabled = false; - smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false); - smbchg_icl_loop_disable_check(chip); - pr_smb(PR_STATUS, "Disabling OTG Boost\n"); - return rc; -} - -static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev) -{ - int rc = 0; - u8 reg = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = smbchg_read(chip, ®, chip->bat_if_base + CMD_CHG_REG, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read OTG enable bit rc=%d\n", rc); - return rc; - } - - return (reg & OTG_EN_BIT) ? 1 : 0; -} - -struct regulator_ops smbchg_otg_reg_ops = { - .enable = smbchg_otg_regulator_enable, - .disable = smbchg_otg_regulator_disable, - .is_enabled = smbchg_otg_regulator_is_enable, -}; - -#define USBIN_CHGR_CFG 0xF1 -#define ADAPTER_ALLOWANCE_MASK 0x7 -#define USBIN_ADAPTER_9V 0x3 -#define USBIN_ADAPTER_5V_9V_CONT 0x2 -#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5 -#define HVDCP_EN_BIT BIT(3) -static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc); - return rc; - } - - rc = smbchg_read(chip, &chip->original_usbin_allowance, - chip->usb_chgpth_base + USBIN_CHGR_CFG, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc); - return rc; - } - - /* - * To disallow source detect and usbin_uv interrupts, set the adapter - * allowance to 9V, so that the audio boost operating in reverse never - * gets detected as a valid input - */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - 0xFF, USBIN_ADAPTER_9V); - if (rc < 0) { - dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Enabling OTG Boost\n"); - return rc; -} - -static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc); - return rc; - } - - /* - * Reenable HVDCP and set the adapter allowance back to the original - * value in order to allow normal USBs to be recognized as a valid - * input. - */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - 0xFF, chip->original_usbin_allowance); - if (rc < 0) { - dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Disabling OTG Boost\n"); - return rc; -} - -static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev) -{ - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER); -} - -struct regulator_ops smbchg_external_otg_reg_ops = { - .enable = smbchg_external_otg_regulator_enable, - .disable = smbchg_external_otg_regulator_disable, - .is_enabled = smbchg_external_otg_regulator_is_enable, -}; - -static int smbchg_regulator_init(struct smbchg_chip *chip) -{ - int rc = 0; - struct regulator_config cfg = {}; - struct device_node *regulator_node; - - cfg.dev = chip->dev; - cfg.driver_data = chip; - - chip->otg_vreg.rdesc.owner = THIS_MODULE; - chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE; - chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops; - chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg"; - chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg"; - - chip->otg_vreg.rdev = devm_regulator_register(chip->dev, - &chip->otg_vreg.rdesc, &cfg); - if (IS_ERR(chip->otg_vreg.rdev)) { - rc = PTR_ERR(chip->otg_vreg.rdev); - chip->otg_vreg.rdev = NULL; - if (rc != -EPROBE_DEFER) - dev_err(chip->dev, - "OTG reg failed, rc=%d\n", rc); - } - if (rc) - return rc; - - regulator_node = of_get_child_by_name(chip->dev->of_node, - "qcom,smbcharger-external-otg"); - if (!regulator_node) { - dev_dbg(chip->dev, "external-otg node absent\n"); - return 0; - } - - chip->ext_otg_vreg.rdesc.owner = THIS_MODULE; - chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE; - chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops; - chip->ext_otg_vreg.rdesc.of_match = "qcom,smbcharger-external-otg"; - chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg"; - if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL)) - chip->ext_otg_vreg.rdesc.supply_name = "otg-parent"; - cfg.dev = chip->dev; - cfg.driver_data = chip; - - chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev, - &chip->ext_otg_vreg.rdesc, - &cfg); - if (IS_ERR(chip->ext_otg_vreg.rdev)) { - rc = PTR_ERR(chip->ext_otg_vreg.rdev); - chip->ext_otg_vreg.rdev = NULL; - if (rc != -EPROBE_DEFER) - dev_err(chip->dev, - "external OTG reg failed, rc=%d\n", rc); - } - - return rc; -} - -#define CMD_CHG_LED_REG 0x43 -#define CHG_LED_CTRL_BIT BIT(0) -#define LED_SW_CTRL_BIT 0x1 -#define LED_CHG_CTRL_BIT 0x0 -#define CHG_LED_ON 0x03 -#define CHG_LED_OFF 0x00 -#define LED_BLINKING_PATTERN1 0x01 -#define LED_BLINKING_PATTERN2 0x02 -#define LED_BLINKING_CFG_MASK SMB_MASK(2, 1) -#define CHG_LED_SHIFT 1 -static int smbchg_chg_led_controls(struct smbchg_chip *chip) -{ - u8 reg, mask; - int rc; - - if (chip->cfg_chg_led_sw_ctrl) { - /* turn-off LED by default for software control */ - mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK; - reg = LED_SW_CTRL_BIT; - } else { - mask = CHG_LED_CTRL_BIT; - reg = LED_CHG_CTRL_BIT; - } - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG, - mask, reg); - if (rc < 0) - dev_err(chip->dev, - "Couldn't write LED_CTRL_BIT rc=%d\n", rc); - return rc; -} - -static void smbchg_chg_led_brightness_set(struct led_classdev *cdev, - enum led_brightness value) -{ - struct smbchg_chip *chip = container_of(cdev, - struct smbchg_chip, led_cdev); - union power_supply_propval pval = {0, }; - u8 reg; - int rc; - - reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT : - CHG_LED_OFF << CHG_LED_SHIFT; - pval.intval = value > LED_OFF ? 1 : 0; - power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER, - &pval); - pr_smb(PR_STATUS, - "set the charger led brightness to value=%d\n", - value); - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + CMD_CHG_LED_REG, - LED_BLINKING_CFG_MASK, reg); - if (rc) - dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n", - rc); -} - -static enum -led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev) -{ - struct smbchg_chip *chip = container_of(cdev, - struct smbchg_chip, led_cdev); - u8 reg_val, chg_led_sts; - int rc; - - rc = smbchg_read(chip, ®_val, chip->bat_if_base + CMD_CHG_LED_REG, - 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read CHG_LED_REG sts rc=%d\n", - rc); - return rc; - } - - chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT; - - pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts); - - return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL; -} - -static void smbchg_chg_led_blink_set(struct smbchg_chip *chip, - unsigned long blinking) -{ - union power_supply_propval pval = {0, }; - u8 reg; - int rc; - - pval.intval = (blinking == 0) ? 0 : 1; - power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER, - &pval); - - if (blinking == 0) { - reg = CHG_LED_OFF << CHG_LED_SHIFT; - } else { - if (blinking == 1) - reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT; - else if (blinking == 2) - reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT; - else - reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT; - } - - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + CMD_CHG_LED_REG, - LED_BLINKING_CFG_MASK, reg); - if (rc) - dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n", - rc); -} - -static ssize_t smbchg_chg_led_blink_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct led_classdev *cdev = dev_get_drvdata(dev); - struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip, - led_cdev); - unsigned long blinking; - ssize_t rc = -EINVAL; - - rc = kstrtoul(buf, 10, &blinking); - if (rc) - return rc; - - smbchg_chg_led_blink_set(chip, blinking); - - return len; -} - -static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store); - -static struct attribute *led_blink_attributes[] = { - &dev_attr_blink.attr, - NULL, -}; - -static struct attribute_group smbchg_led_attr_group = { - .attrs = led_blink_attributes -}; - -static int smbchg_register_chg_led(struct smbchg_chip *chip) -{ - int rc; - - chip->led_cdev.name = "red"; - chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set; - chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get; - - rc = led_classdev_register(chip->dev, &chip->led_cdev); - if (rc) { - dev_err(chip->dev, "unable to register charger led, rc=%d\n", - rc); - return rc; - } - - rc = sysfs_create_group(&chip->led_cdev.dev->kobj, - &smbchg_led_attr_group); - if (rc) { - dev_err(chip->dev, "led sysfs rc: %d\n", rc); - return rc; - } - - return rc; -} - -static int vf_adjust_low_threshold = 5; -module_param(vf_adjust_low_threshold, int, 0644); - -static int vf_adjust_high_threshold = 7; -module_param(vf_adjust_high_threshold, int, 0644); - -static int vf_adjust_n_samples = 10; -module_param(vf_adjust_n_samples, int, 0644); - -static int vf_adjust_max_delta_mv = 40; -module_param(vf_adjust_max_delta_mv, int, 0644); - -static int vf_adjust_trim_steps_per_adjust = 1; -module_param(vf_adjust_trim_steps_per_adjust, int, 0644); - -#define CENTER_TRIM_CODE 7 -#define MAX_LIN_CODE 14 -#define MAX_TRIM_CODE 15 -#define SCALE_SHIFT 4 -#define VF_TRIM_OFFSET_MASK SMB_MASK(3, 0) -#define VF_STEP_SIZE_MV 10 -#define SCALE_LSB_MV 17 -static int smbchg_trim_add_steps(int prev_trim, int delta_steps) -{ - int scale_steps; - int linear_offset, linear_scale; - int offset_code = prev_trim & VF_TRIM_OFFSET_MASK; - int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT; - - if (abs(delta_steps) > 1) { - pr_smb(PR_STATUS, - "Cant trim multiple steps delta_steps = %d\n", - delta_steps); - return prev_trim; - } - if (offset_code <= CENTER_TRIM_CODE) - linear_offset = offset_code + CENTER_TRIM_CODE; - else if (offset_code > CENTER_TRIM_CODE) - linear_offset = MAX_TRIM_CODE - offset_code; - - if (scale_code <= CENTER_TRIM_CODE) - linear_scale = scale_code + CENTER_TRIM_CODE; - else if (scale_code > CENTER_TRIM_CODE) - linear_scale = scale_code - (CENTER_TRIM_CODE + 1); - - /* check if we can accomodate delta steps with just the offset */ - if (linear_offset + delta_steps >= 0 - && linear_offset + delta_steps <= MAX_LIN_CODE) { - linear_offset += delta_steps; - - if (linear_offset > CENTER_TRIM_CODE) - offset_code = linear_offset - CENTER_TRIM_CODE; - else - offset_code = MAX_TRIM_CODE - linear_offset; - - return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code; - } - - /* changing offset cannot satisfy delta steps, change the scale bits */ - scale_steps = delta_steps > 0 ? 1 : -1; - - if (linear_scale + scale_steps < 0 - || linear_scale + scale_steps > MAX_LIN_CODE) { - pr_smb(PR_STATUS, - "Cant trim scale_steps = %d delta_steps = %d\n", - scale_steps, delta_steps); - return prev_trim; - } - - linear_scale += scale_steps; - - if (linear_scale > CENTER_TRIM_CODE) - scale_code = linear_scale - CENTER_TRIM_CODE; - else - scale_code = linear_scale + (CENTER_TRIM_CODE + 1); - prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK) - | scale_code << SCALE_SHIFT; - - /* - * now that we have changed scale which is a 17mV jump, change the - * offset bits (10mV) too so the effective change is just 7mV - */ - delta_steps = -1 * delta_steps; - - linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE); - if (linear_offset > CENTER_TRIM_CODE) - offset_code = linear_offset - CENTER_TRIM_CODE; - else - offset_code = MAX_TRIM_CODE - linear_offset; - - return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code; -} - -#define TRIM_14 0xFE -#define VF_TRIM_MASK 0xFF -static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip, - int delta_mv) -{ - int sign, delta_steps, rc = 0; - u8 prev_trim, new_trim; - int i; - - sign = delta_mv > 0 ? 1 : -1; - delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2) - / VF_STEP_SIZE_MV; - - rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1); - if (rc) { - dev_err(chip->dev, "Unable to read trim 14: %d\n", rc); - return rc; - } - - for (i = 1; i <= abs(delta_steps) - && i <= vf_adjust_trim_steps_per_adjust; i++) { - new_trim = (u8)smbchg_trim_add_steps(prev_trim, - delta_steps > 0 ? 1 : -1); - if (new_trim == prev_trim) { - pr_smb(PR_STATUS, - "VFloat trim unchanged from %02x\n", prev_trim); - /* treat no trim change as an error */ - return -EINVAL; - } - - rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14, - VF_TRIM_MASK, new_trim); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't change vfloat trim rc=%d\n", rc); - } - pr_smb(PR_STATUS, - "VFlt trim %02x to %02x, delta steps: %d\n", - prev_trim, new_trim, delta_steps); - prev_trim = new_trim; - } - - return rc; -} - -#define VFLOAT_RESAMPLE_DELAY_MS 10000 -static void smbchg_vfloat_adjust_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - vfloat_adjust_work.work); - int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv; - bool taper, enable; - - smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST); - taper = (get_prop_charge_type(chip) - == POWER_SUPPLY_CHARGE_TYPE_TAPER); - enable = taper && (chip->parallel.current_max_ma == 0); - - if (!enable) { - pr_smb(PR_MISC, - "Stopping vfloat adj taper=%d parallel_ma = %d\n", - taper, chip->parallel.current_max_ma); - goto stop; - } - - if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) { - pr_smb(PR_STATUS, "JEITA active, skipping\n"); - goto stop; - } - - set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1); - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv); - if (rc) { - pr_smb(PR_STATUS, - "bms psy does not support voltage rc = %d\n", rc); - goto stop; - } - vbat_mv = vbat_uv / 1000; - - if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) { - pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv); - goto reschedule; - } - - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua); - if (rc) { - pr_smb(PR_STATUS, - "bms psy does not support current_now rc = %d\n", rc); - goto stop; - } - - if (ibat_ua / 1000 > -chip->iterm_ma) { - pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua); - goto reschedule; - } - - pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n", - chip->n_vbat_samples, - vbat_mv, - ibat_ua); - - chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv); - chip->n_vbat_samples += 1; - if (chip->n_vbat_samples < vf_adjust_n_samples) { - pr_smb(PR_STATUS, "Skip %d samples; max = %d\n", - chip->n_vbat_samples, chip->max_vbat_sample); - goto reschedule; - } - /* if max vbat > target vfloat, delta_vfloat_mv could be negative */ - delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample; - pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n", - delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample); - /* - * enough valid samples has been collected, adjust trim codes - * based on maximum of collected vbat samples if necessary - */ - if (delta_vfloat_mv > vf_adjust_high_threshold - || delta_vfloat_mv < -1 * vf_adjust_low_threshold) { - rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv); - if (rc) { - pr_smb(PR_STATUS, - "Stopping vfloat adj after trim adj rc = %d\n", - rc); - goto stop; - } - chip->max_vbat_sample = 0; - chip->n_vbat_samples = 0; - goto reschedule; - } - -stop: - chip->max_vbat_sample = 0; - chip->n_vbat_samples = 0; - smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST); - return; - -reschedule: - schedule_delayed_work(&chip->vfloat_adjust_work, - msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS)); - return; -} - -static int smbchg_charging_status_change(struct smbchg_chip *chip) -{ - smbchg_vfloat_adjust_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - return 0; -} - -#define BB_CLMP_SEL 0xF8 -#define BB_CLMP_MASK SMB_MASK(1, 0) -#define BB_CLMP_VFIX_3338MV 0x1 -#define BB_CLMP_VFIX_3512MV 0x2 -static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type) -{ - int rc; - bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP - && smbchg_is_usbin_active_pwr_src(chip)); - - /* - * Set the charger switching freq to 1MHZ if HVDCP 2.0, - * or 750KHZ otherwise - */ - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + BAT_IF_TRIM7_REG, - CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT); - if (rc) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - /* - * Set the charger switch frequency clamp voltage threshold to 3.338V - * if HVDCP 2.0, or 3.512V otherwise. - */ - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL, - BB_CLMP_MASK, - hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV); - if (rc) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - return 0; -} - -#define DEFAULT_SDP_MA 100 -#define DEFAULT_CDP_MA 1500 -static int smbchg_change_usb_supply_type(struct smbchg_chip *chip, - enum power_supply_type type) -{ - int rc, current_limit_ma; - - /* - * if the type is not unknown, set the type before changing ICL vote - * in order to ensure that the correct current limit registers are - * used - */ - if (type != POWER_SUPPLY_TYPE_UNKNOWN) - chip->usb_supply_type = type; - - /* - * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current - * modes, skip all BC 1.2 current if external typec is supported. - * Note: for SDP supporting current based on USB notifications. - */ - if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB)) - current_limit_ma = chip->typec_current_ma; - else if (type == POWER_SUPPLY_TYPE_USB) - current_limit_ma = DEFAULT_SDP_MA; - else if (type == POWER_SUPPLY_TYPE_USB_CDP) - current_limit_ma = DEFAULT_CDP_MA; - else if (type == POWER_SUPPLY_TYPE_USB_HVDCP) - current_limit_ma = smbchg_default_hvdcp_icl_ma; - else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3) - current_limit_ma = smbchg_default_hvdcp3_icl_ma; - else - current_limit_ma = smbchg_default_dcp_icl_ma; - - pr_smb(PR_STATUS, "Type %d: setting mA = %d\n", - type, current_limit_ma); - rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, - current_limit_ma); - if (rc < 0) { - pr_err("Couldn't vote for new USB ICL rc=%d\n", rc); - goto out; - } - - /* otherwise if it is unknown, set type after the vote */ - if (type == POWER_SUPPLY_TYPE_UNKNOWN) - chip->usb_supply_type = type; - - if (!chip->skip_usb_notification) - power_supply_changed(chip->usb_psy); - - /* set the correct buck switching frequency */ - rc = smbchg_set_optimal_charging_mode(chip, type); - if (rc < 0) - pr_err("Couldn't set charger optimal mode rc=%d\n", rc); - -out: - return rc; -} - -#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4) -#define HVDCP_5V 0x00 -#define HVDCP_9V 0x10 -#define USB_CMD_HVDCP_1 0x42 -#define FORCE_HVDCP_2p0 BIT(3) - -static int force_9v_hvdcp(struct smbchg_chip *chip) -{ - int rc; - - /* Force 5V HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc) { - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc); - return rc; - } - - /* Force QC2.0 */ - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + USB_CMD_HVDCP_1, - FORCE_HVDCP_2p0, FORCE_HVDCP_2p0); - rc |= smbchg_masked_write(chip, - chip->usb_chgpth_base + USB_CMD_HVDCP_1, - FORCE_HVDCP_2p0, 0); - if (rc < 0) { - pr_err("Couldn't force QC2.0 rc=%d\n", rc); - return rc; - } - - /* Delay to switch into HVDCP 2.0 and avoid UV */ - msleep(500); - - /* Force 9V HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc) - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc); - - return rc; -} - -static void smbchg_hvdcp_det_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - hvdcp_det_work.work); - int rc; - - if (is_hvdcp_present(chip)) { - if (!chip->hvdcp3_supported && - (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) { - /* force HVDCP 2.0 */ - rc = force_9v_hvdcp(chip); - if (rc) - pr_err("could not force 9V HVDCP continuing rc=%d\n", - rc); - } - smbchg_change_usb_supply_type(chip, - POWER_SUPPLY_TYPE_USB_HVDCP); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_aicl_deglitch_wa_check(chip); - } - smbchg_relax(chip, PM_DETECT_HVDCP); -} - -static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state) -{ - int rc; - u8 reg; - union power_supply_propval pval = {0, }; - - /* - * ensure that we are not in the middle of an insertion where usbin_uv - * is low and src_detect hasnt gone high. If so force dp=F dm=F - * which guarantees proper type detection - */ - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_MISC, "overwriting state = %d with %d\n", - state, POWER_SUPPLY_DP_DM_DPF_DMF); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - return regulator_enable(chip->dpdm_reg); - } - pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state); - pval.intval = state; - return power_supply_set_property(chip->usb_psy, - POWER_SUPPLY_PROP_DP_DM, &pval); -} - -#define APSD_CFG 0xF5 -#define AUTO_SRC_DETECT_EN_BIT BIT(0) -#define APSD_TIMEOUT_MS 1500 -static void restore_from_hvdcp_detection(struct smbchg_chip *chip) -{ - int rc; - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - /* switch to 9V HVDCP */ - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) - pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc); - - /* enable HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable HVDCP rc=%d\n", rc); - - /* enable APSD */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable APSD rc=%d\n", rc); - - /* Reset back to 5V unregulated */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V); - if (rc < 0) - pr_err("Couldn't write usb allowance rc=%d\n", rc); - - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable AICL rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = false; - chip->pulse_cnt = 0; -} - -#define RESTRICTED_CHG_FCC_PERCENT 50 -static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable) -{ - int current_table_index, fastchg_current; - int rc = 0; - - /* If enable, set the fcc to the set point closest - * to 50% of the configured fcc while remaining below it - */ - current_table_index = find_smaller_in_array( - chip->tables.usb_ilim_ma_table, - chip->cfg_fastchg_current_ma - * RESTRICTED_CHG_FCC_PERCENT / 100, - chip->tables.usb_ilim_ma_len); - fastchg_current = - chip->tables.usb_ilim_ma_table[current_table_index]; - rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable, - fastchg_current); - - pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable); - chip->restricted_charging = enable; - - return rc; -} - -static void handle_usb_removal(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int rc; - - pr_smb(PR_STATUS, "triggered\n"); - smbchg_aicl_deglitch_wa_check(chip); - /* Clear the OV detected status set before */ - if (chip->usb_ov_det) - chip->usb_ov_det = false; - /* Clear typec current status */ - if (chip->typec_psy) - chip->typec_current_ma = 0; - smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); - extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present); - if (chip->dpdm_reg) - regulator_disable(chip->dpdm_reg); - schedule_work(&chip->usb_set_online_work); - - pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; - power_supply_changed(chip->usb_psy); - - if (parallel_psy && chip->parallel_charger_detected) { - pval.intval = false; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - } - if (chip->parallel.avail && chip->aicl_done_irq - && chip->enable_aicl_wake) { - disable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = false; - } - chip->parallel.enabled_once = false; - chip->vbat_above_headroom = false; - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - ICL_OVERRIDE_BIT, 0); - if (rc < 0) - pr_err("Couldn't set override rc = %d\n", rc); - - vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0); - chip->usb_icl_delta = 0; - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0); - vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, false, 0); - if (!chip->hvdcp_not_supported) - restore_from_hvdcp_detection(chip); -} - -static bool is_usbin_uv_high(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - return reg &= USBIN_UV_BIT; -} - -#define HVDCP_NOTIFY_MS 2500 -static void handle_usb_insertion(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - enum power_supply_type usb_supply_type; - int rc; - char *usb_type_name = "null"; - - pr_smb(PR_STATUS, "triggered\n"); - /* usb inserted */ - read_usb_type(chip, &usb_type_name, &usb_supply_type); - pr_smb(PR_STATUS, - "inserted type = %d (%s)", usb_supply_type, usb_type_name); - - smbchg_aicl_deglitch_wa_check(chip); - if (chip->typec_psy) - update_typec_status(chip); - smbchg_change_usb_supply_type(chip, usb_supply_type); - - /* Only notify USB if it's not a charger */ - if (usb_supply_type == POWER_SUPPLY_TYPE_USB || - usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP) - extcon_set_cable_state_(chip->extcon, EXTCON_USB, - chip->usb_present); - - /* Notify the USB psy if OV condition is not present */ - if (!chip->usb_ov_det) { - /* - * Note that this could still be a very weak charger - * if the handle_usb_insertion was triggered from - * the falling edge of an USBIN_OV interrupt - */ - pr_smb(PR_MISC, "setting usb psy health %s\n", - chip->very_weak_charger - ? "UNSPEC_FAILURE" : "GOOD"); - chip->usb_health = chip->very_weak_charger - ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE - : POWER_SUPPLY_HEALTH_GOOD; - power_supply_changed(chip->usb_psy); - } - schedule_work(&chip->usb_set_online_work); - - if (!chip->hvdcp_not_supported && - (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) { - cancel_delayed_work_sync(&chip->hvdcp_det_work); - smbchg_stay_awake(chip, PM_DETECT_HVDCP); - schedule_delayed_work(&chip->hvdcp_det_work, - msecs_to_jiffies(HVDCP_NOTIFY_MS)); - } - - if (parallel_psy) { - pval.intval = true; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - chip->parallel_charger_detected = rc ? false : true; - if (rc) - pr_debug("parallel-charger absent rc=%d\n", rc); - } - - if (chip->parallel.avail && chip->aicl_done_irq - && !chip->enable_aicl_wake) { - rc = enable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = true; - } -} - -void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force) -{ - mutex_lock(&chip->usb_status_lock); - if (force) { - chip->usb_present = usb_present; - chip->usb_present ? handle_usb_insertion(chip) - : handle_usb_removal(chip); - goto unlock; - } - if (!chip->usb_present && usb_present) { - chip->usb_present = usb_present; - handle_usb_insertion(chip); - } else if (chip->usb_present && !usb_present) { - chip->usb_present = usb_present; - handle_usb_removal(chip); - } - - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); -unlock: - mutex_unlock(&chip->usb_status_lock); -} - -static int otg_oc_reset(struct smbchg_chip *chip) -{ - int rc; - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, 0); - if (rc) - pr_err("Failed to disable OTG rc=%d\n", rc); - - msleep(20); - - /* - * There is a possibility that an USBID interrupt might have - * occurred notifying USB power supply to disable OTG. We - * should not enable OTG in such cases. - */ - if (!is_otg_present(chip)) { - pr_smb(PR_STATUS, - "OTG is not present, not enabling OTG_EN_BIT\n"); - goto out; - } - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, OTG_EN_BIT); - if (rc) - pr_err("Failed to re-enable OTG rc=%d\n", rc); - -out: - return rc; -} - -static int get_current_time(unsigned long *now_tm_sec) -{ - struct rtc_time tm; - struct rtc_device *rtc; - int rc; - - rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); - if (rtc == NULL) { - pr_err("%s: unable to open rtc device (%s)\n", - __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); - return -EINVAL; - } - - rc = rtc_read_time(rtc, &tm); - if (rc) { - pr_err("Error reading rtc device (%s) : %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - - rc = rtc_valid_tm(&tm); - if (rc) { - pr_err("Invalid RTC time (%s): %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - rtc_tm_to_time(&tm, now_tm_sec); - -close_time: - rtc_class_close(rtc); - return rc; -} - -#define AICL_IRQ_LIMIT_SECONDS 60 -#define AICL_IRQ_LIMIT_COUNT 25 -static void increment_aicl_count(struct smbchg_chip *chip) -{ - bool bad_charger = false; - int max_aicl_count, rc; - u8 reg; - long elapsed_seconds; - unsigned long now_seconds; - - pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n", - chip->aicl_irq_count, chip->aicl_deglitch_short, - chip->first_aicl_seconds); - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_1_REG, 1); - if (!rc) - chip->aicl_complete = reg & AICL_STS_BIT; - else - chip->aicl_complete = false; - - if (chip->aicl_deglitch_short || chip->force_aicl_rerun) { - if (!chip->aicl_irq_count) - get_current_time(&chip->first_aicl_seconds); - get_current_time(&now_seconds); - elapsed_seconds = now_seconds - - chip->first_aicl_seconds; - - if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) { - pr_smb(PR_INTERRUPT, - "resetting: elp:%ld first:%ld now:%ld c=%d\n", - elapsed_seconds, chip->first_aicl_seconds, - now_seconds, chip->aicl_irq_count); - chip->aicl_irq_count = 1; - get_current_time(&chip->first_aicl_seconds); - return; - } - /* - * Double the amount of AICLs allowed if parallel charging is - * enabled. - */ - max_aicl_count = AICL_IRQ_LIMIT_COUNT - * (chip->parallel.avail ? 2 : 1); - chip->aicl_irq_count++; - - if (chip->aicl_irq_count > max_aicl_count) { - pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n", - elapsed_seconds, chip->first_aicl_seconds, - now_seconds, chip->aicl_irq_count); - pr_smb(PR_INTERRUPT, "Disable AICL rerun\n"); - chip->very_weak_charger = true; - bad_charger = true; - - /* - * Disable AICL rerun since many interrupts were - * triggered in a short time - */ - /* disable hw aicl */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, true, 0); - if (rc < 0) { - pr_err("Couldn't disable hw aicl rerun rc=%d\n", - rc); - return; - } - - /* Vote 100mA current limit */ - rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, - true, CURRENT_100_MA); - if (rc < 0) { - pr_err("Can't vote %d current limit rc=%d\n", - CURRENT_100_MA, rc); - } - - chip->aicl_irq_count = 0; - } else if ((get_prop_charge_type(chip) == - POWER_SUPPLY_CHARGE_TYPE_FAST) && - (reg & AICL_SUSP_BIT)) { - /* - * If the AICL_SUSP_BIT is on, then AICL reruns have - * already been disabled. Set the very weak charger - * flag so that the driver reports a bad charger - * and does not reenable AICL reruns. - */ - chip->very_weak_charger = true; - bad_charger = true; - } - if (bad_charger) { - pr_smb(PR_MISC, - "setting usb psy health UNSPEC_FAILURE\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - power_supply_changed(chip->usb_psy); - schedule_work(&chip->usb_set_online_work); - } - } -} - -static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high) -{ - int rc; - int tries = 3; - struct completion *completion = &chip->usbin_uv_lowered; - bool usbin_uv; - - if (high) - completion = &chip->usbin_uv_raised; - - while (tries--) { - rc = wait_for_completion_interruptible_timeout( - completion, - msecs_to_jiffies(APSD_TIMEOUT_MS)); - if (rc >= 0) - break; - } - - usbin_uv = is_usbin_uv_high(chip); - - if (high == usbin_uv) - return 0; - - pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n", - high ? "risen" : "lowered", - usbin_uv ? "high" : "low", - tries, rc); - return -EINVAL; -} - -static int wait_for_src_detect(struct smbchg_chip *chip, bool high) -{ - int rc; - int tries = 3; - struct completion *completion = &chip->src_det_lowered; - bool src_detect; - - if (high) - completion = &chip->src_det_raised; - - while (tries--) { - rc = wait_for_completion_interruptible_timeout( - completion, - msecs_to_jiffies(APSD_TIMEOUT_MS)); - if (rc >= 0) - break; - } - - src_detect = is_src_detect_high(chip); - - if (high == src_detect) - return 0; - - pr_err("src detect didnt go to a %s state, still at %s, tries = %d, rc = %d\n", - high ? "risen" : "lowered", - src_detect ? "high" : "low", - tries, rc); - return -EINVAL; -} - -static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion) -{ - int rc; - bool src_detect; - bool usbin_uv; - - if (insertion) { - reinit_completion(&chip->src_det_raised); - reinit_completion(&chip->usbin_uv_lowered); - } else { - reinit_completion(&chip->src_det_lowered); - reinit_completion(&chip->usbin_uv_raised); - } - - /* ensure that usbin uv real time status is in the right state */ - usbin_uv = is_usbin_uv_high(chip); - if (usbin_uv != insertion) { - pr_err("Skip faking, usbin uv is already %d\n", usbin_uv); - return -EINVAL; - } - - /* ensure that src_detect real time status is in the right state */ - src_detect = is_src_detect_high(chip); - if (src_detect == insertion) { - pr_err("Skip faking, src detect is already %d\n", src_detect); - return -EINVAL; - } - - pr_smb(PR_MISC, "Allow only %s charger\n", - insertion ? "5-9V" : "9V only"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - ADAPTER_ALLOWANCE_MASK, - insertion ? - USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V); - if (rc < 0) { - pr_err("Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on %s usbin uv\n", - insertion ? "falling" : "rising"); - rc = wait_for_usbin_uv(chip, !insertion); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on %s src det\n", - insertion ? "rising" : "falling"); - rc = wait_for_src_detect(chip, insertion); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - return 0; -} - -static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip) -{ - int rc = 0; - u8 reg; - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - goto out; - } - - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - goto out; - } - - /* disable HVDCP */ - pr_smb(PR_MISC, "Disable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable HVDCP rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300); - if (rc < 0) { - pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "Disable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - - chip->hvdcp_3_det_ignore_uv = true; - /* fake a removal */ - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) { - pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc); - goto handle_removal; - } - - /* disable APSD */ - pr_smb(PR_MISC, "Disabling APSD\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable APSD rc=%d\n", rc); - goto out; - } - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) { - pr_err("Couldn't fake insertion rc=%d\n", rc); - goto handle_removal; - } - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_MISC, "Enable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF); - /* - * DCP will switch to HVDCP in this time by removing the short - * between DP DM - */ - msleep(HVDCP_NOTIFY_MS); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and the usb type should be none since APSD was disabled - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 2s sleep\n"); - rc = -EINVAL; - goto out; - } - - smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if ((reg >> TYPE_BITS_OFFSET) != 0) { - pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n"); - rc = -EINVAL; - goto out; - } - - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3); - /* Wait 60mS after entering continuous mode */ - msleep(60); - - return 0; -out: - chip->hvdcp_3_det_ignore_uv = false; - restore_from_hvdcp_detection(chip); - return rc; -handle_removal: - chip->hvdcp_3_det_ignore_uv = false; - update_usb_status(chip, 0, 0); - return rc; -} - -static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip) -{ - int rc = 0; - - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - - /* switch to 9V HVDCP */ - pr_smb(PR_MISC, "Switch to 9V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc); - return rc; - } - - /* enable HVDCP */ - pr_smb(PR_MISC, "Enable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) { - pr_err("Couldn't enable HVDCP rc=%d\n", rc); - return rc; - } - - /* enable APSD */ - pr_smb(PR_MISC, "Enabling APSD\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT); - if (rc < 0) { - pr_err("Couldn't enable APSD rc=%d\n", rc); - return rc; - } - - /* Disable AICL */ - pr_smb(PR_MISC, "Disable AICL\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable AICL rc=%d\n", rc); - return rc; - } - - /* fake a removal */ - chip->hvdcp_3_det_ignore_uv = true; - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) { - pr_err("Couldn't fake removal rc=%d\n", rc); - goto out; - } - - /* - * reset the enabled once flag for parallel charging so - * parallel charging can immediately restart after the HVDCP pulsing - * is complete - */ - chip->parallel.enabled_once = false; - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) { - pr_err("Couldn't fake insertion rc=%d\n", rc); - goto out; - } - chip->hvdcp_3_det_ignore_uv = false; - - /* Enable AICL */ - pr_smb(PR_MISC, "Enable AICL\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't enable AICL rc=%d\n", rc); - return rc; - } - -out: - /* - * There are many QC 2.0 chargers that collapse before the aicl deglitch - * timer can mitigate. Hence set the aicl deglitch time to a shorter - * period. - */ - - rc = vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc); - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = false; - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "HVDCP removed\n"); - update_usb_status(chip, 0, 0); - } - return rc; -} - -#define USB_CMD_APSD 0x41 -#define APSD_RERUN BIT(0) -static int rerun_apsd(struct smbchg_chip *chip) -{ - int rc; - - reinit_completion(&chip->src_det_raised); - reinit_completion(&chip->usbin_uv_lowered); - reinit_completion(&chip->src_det_lowered); - reinit_completion(&chip->usbin_uv_raised); - - /* re-run APSD */ - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD, - APSD_RERUN, APSD_RERUN); - if (rc) { - pr_err("Couldn't re-run APSD rc=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on rising usbin uv\n"); - rc = wait_for_usbin_uv(chip, true); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on falling src det\n"); - rc = wait_for_src_detect(chip, false); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on falling usbin uv\n"); - rc = wait_for_usbin_uv(chip, false); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on rising src det\n"); - rc = wait_for_src_detect(chip, true); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - return rc; -} - -#define SCHG_LITE_USBIN_HVDCP_5_9V 0x8 -#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK 0x38 -#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE BIT(3) -static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip) -{ - int rc; - u8 reg = 0; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc) { - pr_err("Unable to read HVDCP status rc=%d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "HVDCP status = %x\n", reg); - - if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) { - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + INPUT_STS, 1); - if (rc) { - pr_err("Unable to read INPUT status rc=%d\n", rc); - return false; - } - pr_smb(PR_STATUS, "INPUT status = %x\n", reg); - if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) == - SCHG_LITE_USBIN_HVDCP_5_9V) - return true; - } - return false; -} - -static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - /* check if HVDCP is already in 5V continuous mode */ - if (is_hvdcp_5v_cont_mode(chip)) { - pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n"); - return 0; - } - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - goto out; - } - - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - goto out; - } - - pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300); - if (rc < 0) { - pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "Disable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - - chip->hvdcp_3_det_ignore_uv = true; - - /* re-run APSD */ - rc = rerun_apsd(chip); - if (rc) { - pr_err("APSD rerun failed\n"); - goto out; - } - - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_MISC, "Enable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - /* - * DCP will switch to HVDCP in this time by removing the short - * between DP DM - */ - msleep(HVDCP_NOTIFY_MS); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and the usb type should be none since APSD was disabled - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 2s sleep\n"); - rc = -EINVAL; - goto out; - } - - /* We are set if HVDCP in 5V continuous mode */ - if (!is_hvdcp_5v_cont_mode(chip)) { - pr_err("HVDCP could not be set in 5V continuous mode\n"); - goto out; - } - - return 0; -out: - chip->hvdcp_3_det_ignore_uv = false; - restore_from_hvdcp_detection(chip); - return rc; -} - -static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n"); - rc = force_9v_hvdcp(chip); - if (rc) { - pr_err("Failed to force 9V HVDCP=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - return rc; -} - -#define CMD_HVDCP_2 0x43 -#define SINGLE_INCREMENT BIT(0) -#define SINGLE_DECREMENT BIT(1) -static int smbchg_dp_pulse_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Increment DP\n"); - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2, - SINGLE_INCREMENT, SINGLE_INCREMENT); - if (rc) - pr_err("Single-increment failed rc=%d\n", rc); - - return rc; -} - -static int smbchg_dm_pulse_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Decrement DM\n"); - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2, - SINGLE_DECREMENT, SINGLE_DECREMENT); - if (rc) - pr_err("Single-decrement failed rc=%d\n", rc); - - return rc; -} - -static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip) -{ - int rc = 0; - - /* - * reset the enabled once flag for parallel charging because this is - * effectively a new insertion. - */ - chip->parallel.enabled_once = false; - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3); - - return rc; -} - -static int smbchg_dp_dm(struct smbchg_chip *chip, int val) -{ - int rc = 0; - int target_icl_vote_ma; - - switch (val) { - case POWER_SUPPLY_DP_DM_PREPARE: - if (!is_hvdcp_present(chip)) { - pr_err("No pulsing unless HVDCP\n"); - return -ENODEV; - } - if (chip->schg_version == QPNP_SCHG_LITE) - rc = smbchg_prepare_for_pulsing_lite(chip); - else - rc = smbchg_prepare_for_pulsing(chip); - break; - case POWER_SUPPLY_DP_DM_UNPREPARE: - if (chip->schg_version == QPNP_SCHG_LITE) - rc = smbchg_unprepare_for_pulsing_lite(chip); - else - rc = smbchg_unprepare_for_pulsing(chip); - break; - case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3: - rc = smbchg_hvdcp3_confirmed(chip); - break; - case POWER_SUPPLY_DP_DM_DP_PULSE: - if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DP_PULSE); - else - rc = smbchg_dp_pulse_lite(chip); - if (!rc) - chip->pulse_cnt++; - pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt); - break; - case POWER_SUPPLY_DP_DM_DM_PULSE: - if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DM_PULSE); - else - rc = smbchg_dm_pulse_lite(chip); - if (!rc && chip->pulse_cnt) - chip->pulse_cnt--; - pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt); - break; - case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED: - chip->hvdcp3_supported = true; - pr_smb(PR_MISC, "HVDCP3 supported\n"); - break; - case POWER_SUPPLY_DP_DM_ICL_DOWN: - chip->usb_icl_delta -= 100; - target_icl_vote_ma = get_client_vote(chip->usb_icl_votable, - PSY_ICL_VOTER); - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true, - target_icl_vote_ma + chip->usb_icl_delta); - break; - case POWER_SUPPLY_DP_DM_ICL_UP: - chip->usb_icl_delta += 100; - target_icl_vote_ma = get_client_vote(chip->usb_icl_votable, - PSY_ICL_VOTER); - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true, - target_icl_vote_ma + chip->usb_icl_delta); - break; - default: - break; - } - - return rc; -} - -static void update_typec_capability_status(struct smbchg_chip *chip, - const union power_supply_propval *val) -{ - pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval); - - pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma, - val->intval); - chip->typec_current_ma = val->intval; - smbchg_change_usb_supply_type(chip, chip->usb_supply_type); -} - -static void update_typec_otg_status(struct smbchg_chip *chip, int mode, - bool force) -{ - union power_supply_propval pval = {0, }; - pr_smb(PR_TYPEC, "typec mode = %d\n", mode); - - if (mode == POWER_SUPPLY_TYPE_DFP) { - chip->typec_dfp = true; - pval.intval = 1; - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, - chip->typec_dfp); - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - } else if (force || chip->typec_dfp) { - chip->typec_dfp = false; - pval.intval = 0; - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, - chip->typec_dfp); - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - } -} - -static int smbchg_usb_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = chip->usb_current_max; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = chip->usb_present; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = chip->usb_online; - break; - case POWER_SUPPLY_PROP_TYPE: - val->intval = chip->usb_supply_type; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = chip->usb_health; - break; - default: - return -EINVAL; - } - return 0; -} - -static int smbchg_usb_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - chip->usb_current_max = val->intval; - break; - case POWER_SUPPLY_PROP_ONLINE: - chip->usb_online = val->intval; - break; - default: - return -EINVAL; - } - - power_supply_changed(psy); - return 0; -} - -static int -smbchg_usb_is_writeable(struct power_supply *psy, enum power_supply_property psp) -{ - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - return 1; - default: - break; - } - - return 0; -} - - -static char *smbchg_usb_supplicants[] = { - "battery", - "bms", -}; - -static enum power_supply_property smbchg_usb_properties[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CURRENT_MAX, - POWER_SUPPLY_PROP_TYPE, - POWER_SUPPLY_PROP_HEALTH, -}; - -#define CHARGE_OUTPUT_VTG_RATIO 840 -static int smbchg_get_iusb(struct smbchg_chip *chip) -{ - int rc, iusb_ua = -EINVAL; - struct qpnp_vadc_result adc_result; - - if (!is_usb_present(chip) && !is_dc_present(chip)) - return 0; - - if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) { - rc = qpnp_vadc_read(chip->vchg_vadc_dev, - chip->vchg_adc_channel, &adc_result); - if (rc) { - pr_smb(PR_STATUS, - "error in VCHG (channel-%d) read rc = %d\n", - chip->vchg_adc_channel, rc); - return 0; - } - iusb_ua = div_s64(adc_result.physical * 1000, - CHARGE_OUTPUT_VTG_RATIO); - } - - return iusb_ua; -} - -static enum power_supply_property smbchg_battery_properties[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, - POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE, - POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, - POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, - POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, - POWER_SUPPLY_PROP_FLASH_ACTIVE, - POWER_SUPPLY_PROP_FLASH_TRIGGER, - POWER_SUPPLY_PROP_DP_DM, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, - POWER_SUPPLY_PROP_RERUN_AICL, - POWER_SUPPLY_PROP_RESTRICTED_CHARGING, -}; - -static int smbchg_battery_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - int rc = 0; - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER, - !val->intval, 0); - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - rc = vote(chip->usb_suspend_votable, USER_EN_VOTER, - !val->intval, 0); - rc = vote(chip->dc_suspend_votable, USER_EN_VOTER, - !val->intval, 0); - chip->chg_enabled = val->intval; - schedule_work(&chip->usb_set_online_work); - break; - case POWER_SUPPLY_PROP_CAPACITY: - chip->fake_battery_soc = val->intval; - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - break; - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - smbchg_system_temp_level_set(chip, val->intval); - break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - rc = smbchg_float_voltage_set(chip, val->intval); - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - rc = smbchg_safety_timer_enable(chip, val->intval); - break; - case POWER_SUPPLY_PROP_FLASH_ACTIVE: - rc = smbchg_switch_buck_frequency(chip, val->intval); - if (rc) { - pr_err("Couldn't switch buck frequency, rc=%d\n", rc); - /* - * Trigger a panic if there is an error while switching - * buck frequency. This will prevent LS FET damage. - */ - BUG_ON(1); - } - - rc = smbchg_otg_pulse_skip_disable(chip, - REASON_FLASH_ENABLED, val->intval); - break; - case POWER_SUPPLY_PROP_FLASH_TRIGGER: - chip->flash_triggered = !!val->intval; - smbchg_icl_loop_disable_check(chip); - break; - case POWER_SUPPLY_PROP_FORCE_TLIM: - rc = smbchg_force_tlim_en(chip, val->intval); - break; - case POWER_SUPPLY_PROP_DP_DM: - rc = smbchg_dp_dm(chip, val->intval); - break; - case POWER_SUPPLY_PROP_RERUN_AICL: - smbchg_rerun_aicl(chip); - break; - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - rc = smbchg_restricted_charging(chip, val->intval); - break; - case POWER_SUPPLY_PROP_CURRENT_CAPABILITY: - if (chip->typec_psy) - update_typec_capability_status(chip, val); - break; - case POWER_SUPPLY_PROP_TYPEC_MODE: - if (chip->typec_psy) - update_typec_otg_status(chip, val->intval, false); - break; - default: - return -EINVAL; - } - - return rc; -} - -static int smbchg_battery_is_writeable(struct power_supply *psy, - enum power_supply_property prop) -{ - int rc; - - switch (prop) { - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CAPACITY: - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - case POWER_SUPPLY_PROP_DP_DM: - case POWER_SUPPLY_PROP_RERUN_AICL: - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - rc = 1; - break; - default: - rc = 0; - break; - } - return rc; -} - -static int smbchg_battery_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_prop_batt_status(chip); - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = get_prop_batt_present(chip); - break; - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - val->intval - = get_effective_result(chip->battchg_suspend_votable); - if (val->intval < 0) /* no votes */ - val->intval = 1; - else - val->intval = !val->intval; - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = chip->chg_enabled; - break; - case POWER_SUPPLY_PROP_CHARGE_TYPE: - val->intval = get_prop_charge_type(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - val->intval = smbchg_float_voltage_get(chip); - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = get_prop_batt_health(chip); - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX: - val->intval = smbchg_calc_max_flash_current(chip); - break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - val->intval = chip->fastchg_current_ma * 1000; - break; - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - val->intval = chip->therm_lvl_sel; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: - val->intval = smbchg_get_aicl_level_ma(chip) * 1000; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: - val->intval = (int)chip->aicl_complete; - break; - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - val->intval = (int)chip->restricted_charging; - break; - /* properties from fg */ - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_prop_batt_capacity(chip); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = get_prop_batt_current_now(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_prop_batt_voltage_now(chip); - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_prop_batt_temp(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_prop_batt_voltage_max_design(chip); - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - val->intval = chip->safety_timer_en; - break; - case POWER_SUPPLY_PROP_FLASH_ACTIVE: - val->intval = chip->otg_pulse_skip_dis; - break; - case POWER_SUPPLY_PROP_FLASH_TRIGGER: - val->intval = chip->flash_triggered; - break; - case POWER_SUPPLY_PROP_DP_DM: - val->intval = chip->pulse_cnt; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: - val->intval = smbchg_is_input_current_limited(chip); - break; - case POWER_SUPPLY_PROP_RERUN_AICL: - val->intval = 0; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: - val->intval = smbchg_get_iusb(chip); - break; - default: - return -EINVAL; - } - return 0; -} - -static char *smbchg_dc_supplicants[] = { - "bms", -}; - -static enum power_supply_property smbchg_dc_properties[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CURRENT_MAX, -}; - -static int smbchg_dc_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - int rc = 0; - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER, - !val->intval, 0); - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true, - val->intval / 1000); - break; - default: - return -EINVAL; - } - - return rc; -} - -static int smbchg_dc_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_PRESENT: - val->intval = is_dc_present(chip); - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = get_effective_result(chip->dc_suspend_votable); - if (val->intval < 0) /* no votes */ - val->intval = 1; - else - val->intval = !val->intval; - break; - case POWER_SUPPLY_PROP_ONLINE: - /* return if dc is charging the battery */ - val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC) - && (get_prop_batt_status(chip) - == POWER_SUPPLY_STATUS_CHARGING); - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = chip->dc_max_current_ma * 1000; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int smbchg_dc_is_writeable(struct power_supply *psy, - enum power_supply_property prop) -{ - int rc; - - switch (prop) { - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = 1; - break; - default: - rc = 0; - break; - } - return rc; -} - -#define HOT_BAT_HARD_BIT BIT(0) -#define HOT_BAT_SOFT_BIT BIT(1) -#define COLD_BAT_HARD_BIT BIT(2) -#define COLD_BAT_SOFT_BIT BIT(3) -#define BAT_OV_BIT BIT(4) -#define BAT_LOW_BIT BIT(5) -#define BAT_MISSING_BIT BIT(6) -#define BAT_TERM_MISSING_BIT BIT(7) -static irqreturn_t batt_hot_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_cold_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_warm_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_cool_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_pres_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_present = !(reg & BAT_MISSING_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t vbat_low_handler(int irq, void *_chip) -{ - pr_warn_ratelimited("vbat low\n"); - return IRQ_HANDLED; -} - -#define CHG_COMP_SFT_BIT BIT(3) -static irqreturn_t chg_error_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int rc = 0; - u8 reg; - - pr_smb(PR_INTERRUPT, "chg-error triggered\n"); - - rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc); - } else { - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - if (reg & CHG_COMP_SFT_BIT) - set_property_on_fg(chip, - POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED, - 1); - } - - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t fastchg_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - - pr_smb(PR_INTERRUPT, "p2f triggered\n"); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t chg_hot_handler(int irq, void *_chip) -{ - pr_warn_ratelimited("chg hot\n"); - smbchg_wipower_check(_chip); - return IRQ_HANDLED; -} - -static irqreturn_t chg_term_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - - pr_smb(PR_INTERRUPT, "tcc triggered\n"); - /* - * Charge termination is a pulse and not level triggered. That means, - * TCC bit in RT_STS can get cleared by the time this interrupt is - * handled. Instead of relying on that to determine whether the - * charge termination had happened, we've to simply notify the FG - * about this as long as the interrupt is handled. - */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1); - - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - - return IRQ_HANDLED; -} - -static irqreturn_t taper_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - taper_irq_en(chip, false); - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_taper(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t recharge_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - return IRQ_HANDLED; -} - -static irqreturn_t wdog_timeout_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->misc_base + RT_STS, 1); - pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - return IRQ_HANDLED; -} - -/** - * power_ok_handler() - called when the switcher turns on or turns off - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating switcher turning on or off - */ -static irqreturn_t power_ok_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->misc_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - return IRQ_HANDLED; -} - -/** - * dcin_uv_handler() - called when the dc voltage crosses the uv threshold - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating whether dc voltage is uv - */ -#define DCIN_UNSUSPEND_DELAY_MS 1000 -static irqreturn_t dcin_uv_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool dc_present = is_dc_present(chip); - - pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n", - chip->dc_present, dc_present); - - if (chip->dc_present != dc_present) { - /* dc changed */ - chip->dc_present = dc_present; - if (chip->dc_psy_type != -EINVAL && chip->batt_psy) - power_supply_changed(chip->dc_psy); - smbchg_charging_status_change(chip); - smbchg_aicl_deglitch_wa_check(chip); - chip->vbat_above_headroom = false; - } - - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -/** - * usbin_ov_handler() - this is called when an overvoltage condition occurs - * @chip: pointer to smbchg_chip chip - */ -static irqreturn_t usbin_ov_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int rc; - u8 reg; - bool usb_present; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - goto out; - } - - /* OV condition is detected. Notify it to USB psy */ - if (reg & USBIN_OV_BIT) { - chip->usb_ov_det = true; - pr_smb(PR_MISC, "setting usb psy health OV\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - power_supply_changed(chip->usb_psy); - } else { - chip->usb_ov_det = false; - /* If USB is present, then handle the USB insertion */ - usb_present = is_usb_present(chip); - if (usb_present) - update_usb_status(chip, usb_present, false); - } -out: - return IRQ_HANDLED; -} - -/** - * usbin_uv_handler() - this is called when USB charger is removed - * @chip: pointer to smbchg_chip chip - * @rt_stat: the status bit indicating chg insertion/removal - */ -#define ICL_MODE_MASK SMB_MASK(5, 4) -#define ICL_MODE_HIGH_CURRENT 0 -static irqreturn_t usbin_uv_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int aicl_level = smbchg_get_aicl_level_ma(chip); - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc) { - pr_err("could not read rt sts: %d", rc); - goto out; - } - - pr_smb(PR_STATUS, - "%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n", - chip->hvdcp_3_det_ignore_uv ? "Ignoring":"", - chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv, - aicl_level); - - /* - * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is - * not already src_detected and usbin_uv is seen falling - */ - if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_MISC, "setting usb dp=f dm=f\n"); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - } - - if (reg & USBIN_UV_BIT) - complete_all(&chip->usbin_uv_raised); - else - complete_all(&chip->usbin_uv_lowered); - - if (chip->hvdcp_3_det_ignore_uv) - goto out; - - if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_STATUS, "Very weak charger detected\n"); - chip->very_weak_charger = true; - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_2_REG, 1); - if (rc) { - dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", - rc); - goto out; - } - if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) { - /* - * If AICL is not even enabled, this is either an - * SDP or a grossly out of spec charger. Do not - * draw any current from it. - */ - rc = vote(chip->usb_suspend_votable, - WEAK_CHARGER_EN_VOTER, true, 0); - if (rc < 0) - pr_err("could not disable charger: %d", rc); - } else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) { - /* - * we are in a situation where the adapter is not able - * to supply even 300mA. Disable hw aicl reruns else it - * is only a matter of time when we get back here again - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't disable hw aicl rerun rc=%d\n", - rc); - } - pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - power_supply_changed(chip->usb_psy); - schedule_work(&chip->usb_set_online_work); - } - - smbchg_wipower_check(chip); -out: - return IRQ_HANDLED; -} - -/** - * src_detect_handler() - this is called on rising edge when USB charger type - * is detected and on falling edge when USB voltage falls - * below the coarse detect voltage(1V), use it for - * handling USB charger insertion and removal. - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating chg insertion/removal - */ -static irqreturn_t src_detect_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool usb_present = is_usb_present(chip); - bool src_detect = is_src_detect_high(chip); - int rc; - - pr_smb(PR_STATUS, - "%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n", - chip->hvdcp_3_det_ignore_uv ? "Ignoring":"", - chip->usb_present, usb_present, src_detect, - chip->hvdcp_3_det_ignore_uv); - - if (src_detect) - complete_all(&chip->src_det_raised); - else - complete_all(&chip->src_det_lowered); - - if (chip->hvdcp_3_det_ignore_uv) - goto out; - - /* - * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V), - * an input collapse due to AICL will actually cause an USBIN_UV - * interrupt to fire as well. - * - * Handle USB insertions and removals in the source detect handler - * instead of the USBIN_UV handler since the latter is untrustworthy - * when the battery voltage is high. - */ - chip->very_weak_charger = false; - /* - * a src detect marks a new insertion or a real removal, - * vote for enable aicl hw reruns - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc); - - rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0); - if (rc < 0) - pr_err("could not enable charger: %d\n", rc); - - if (src_detect) { - update_usb_status(chip, usb_present, 0); - } else { - update_usb_status(chip, 0, false); - chip->aicl_irq_count = 0; - } -out: - return IRQ_HANDLED; -} - -/** - * otg_oc_handler() - called when the usb otg goes over current - */ -#define NUM_OTG_RETRIES 5 -#define OTG_OC_RETRY_DELAY_US 50000 -static irqreturn_t otg_oc_handler(int irq, void *_chip) -{ - int rc; - struct smbchg_chip *chip = _chip; - s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time); - - pr_smb(PR_INTERRUPT, "triggered\n"); - - if (chip->schg_version == QPNP_SCHG_LITE) { - pr_warn("OTG OC triggered - OTG disabled\n"); - return IRQ_HANDLED; - } - - if (elapsed_us > OTG_OC_RETRY_DELAY_US) - chip->otg_retries = 0; - - /* - * Due to a HW bug in the PMI8994 charger, the current inrush that - * occurs when connecting certain OTG devices can cause the OTG - * overcurrent protection to trip. - * - * The work around is to try reenabling the OTG when getting an - * overcurrent interrupt once. - */ - if (chip->otg_retries < NUM_OTG_RETRIES) { - chip->otg_retries += 1; - pr_smb(PR_STATUS, - "Retrying OTG enable. Try #%d, elapsed_us %lld\n", - chip->otg_retries, elapsed_us); - rc = otg_oc_reset(chip); - if (rc) - pr_err("Failed to reset OTG OC state rc=%d\n", rc); - chip->otg_enable_time = ktime_get(); - } - return IRQ_HANDLED; -} - -/** - * otg_fail_handler() - called when the usb otg fails - * (when vbat < OTG UVLO threshold) - */ -static irqreturn_t otg_fail_handler(int irq, void *_chip) -{ - pr_smb(PR_INTERRUPT, "triggered\n"); - return IRQ_HANDLED; -} - -/** - * aicl_done_handler() - called when the usb AICL algorithm is finished - * and a current is set. - */ -static irqreturn_t aicl_done_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool usb_present = is_usb_present(chip); - int aicl_level = smbchg_get_aicl_level_ma(chip); - - pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level); - - increment_aicl_count(chip); - - if (usb_present) - smbchg_parallel_usb_check_ok(chip); - - if (chip->aicl_complete && chip->batt_psy) - power_supply_changed(chip->batt_psy); - - return IRQ_HANDLED; -} - -/** - * usbid_change_handler() - called when the usb RID changes. - * This is used mostly for detecting OTG - */ -static irqreturn_t usbid_change_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool otg_present; - - pr_smb(PR_INTERRUPT, "triggered\n"); - - otg_present = is_otg_present(chip); - pr_smb(PR_MISC, "setting usb psy OTG = %d\n", - otg_present ? 1 : 0); - - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present); - - if (otg_present) - pr_smb(PR_STATUS, "OTG detected\n"); - - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - - return IRQ_HANDLED; -} - -static int determine_initial_status(struct smbchg_chip *chip) -{ - union power_supply_propval type = {0, }; - - /* - * It is okay to read the interrupt status here since - * interrupts aren't requested. reading interrupt status - * clears the interrupt so be careful to read interrupt - * status only in interrupt handling code - */ - - batt_pres_handler(0, chip); - batt_hot_handler(0, chip); - batt_warm_handler(0, chip); - batt_cool_handler(0, chip); - batt_cold_handler(0, chip); - if (chip->typec_psy) { - get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type); - update_typec_otg_status(chip, type.intval, true); - } else { - usbid_change_handler(0, chip); - } - src_detect_handler(0, chip); - - chip->usb_present = is_usb_present(chip); - chip->dc_present = is_dc_present(chip); - - if (chip->usb_present) { - int rc = 0; - pr_smb(PR_MISC, "setting usb dp=f dm=f\n"); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - handle_usb_insertion(chip); - } else { - handle_usb_removal(chip); - } - - return 0; -} - -static int prechg_time[] = { - 24, - 48, - 96, - 192, -}; -static int chg_time[] = { - 192, - 384, - 768, - 1536, -}; - -enum bpd_type { - BPD_TYPE_BAT_NONE, - BPD_TYPE_BAT_ID, - BPD_TYPE_BAT_THM, - BPD_TYPE_BAT_THM_BAT_ID, - BPD_TYPE_DEFAULT, -}; - -static const char * const bpd_label[] = { - [BPD_TYPE_BAT_NONE] = "bpd_none", - [BPD_TYPE_BAT_ID] = "bpd_id", - [BPD_TYPE_BAT_THM] = "bpd_thm", - [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id", -}; - -static inline int get_bpd(const char *name) -{ - int i = 0; - for (i = 0; i < ARRAY_SIZE(bpd_label); i++) { - if (strcmp(bpd_label[i], name) == 0) - return i; - } - return -EINVAL; -} - -#define REVISION1_REG 0x0 -#define DIG_MINOR 0 -#define DIG_MAJOR 1 -#define ANA_MINOR 2 -#define ANA_MAJOR 3 -#define CHGR_CFG1 0xFB -#define RECHG_THRESHOLD_SRC_BIT BIT(1) -#define TERM_I_SRC_BIT BIT(2) -#define TERM_SRC_FG BIT(2) -#define CHG_INHIB_CFG_REG 0xF7 -#define CHG_INHIBIT_50MV_VAL 0x00 -#define CHG_INHIBIT_100MV_VAL 0x01 -#define CHG_INHIBIT_200MV_VAL 0x02 -#define CHG_INHIBIT_300MV_VAL 0x03 -#define CHG_INHIBIT_MASK 0x03 -#define USE_REGISTER_FOR_CURRENT BIT(2) -#define CHGR_CFG2 0xFC -#define CHG_EN_SRC_BIT BIT(7) -#define CHG_EN_POLARITY_BIT BIT(6) -#define P2F_CHG_TRAN BIT(5) -#define CHG_BAT_OV_ECC BIT(4) -#define I_TERM_BIT BIT(3) -#define AUTO_RECHG_BIT BIT(2) -#define CHARGER_INHIBIT_BIT BIT(0) -#define USB51_COMMAND_POL BIT(2) -#define USB51AC_CTRL BIT(1) -#define TR_8OR32B 0xFE -#define BUCK_8_16_FREQ_BIT BIT(0) -#define BM_CFG 0xF3 -#define BATT_MISSING_ALGO_BIT BIT(2) -#define BMD_PIN_SRC_MASK SMB_MASK(1, 0) -#define PIN_SRC_SHIFT 0 -#define CHGR_CFG 0xFF -#define RCHG_LVL_BIT BIT(0) -#define VCHG_EN_BIT BIT(1) -#define VCHG_INPUT_CURRENT_BIT BIT(3) -#define CFG_AFVC 0xF6 -#define VFLOAT_COMP_ENABLE_MASK SMB_MASK(2, 0) -#define TR_RID_REG 0xFA -#define FG_INPUT_FET_DELAY_BIT BIT(3) -#define TRIM_OPTIONS_7_0 0xF6 -#define INPUT_MISSING_POLLER_EN_BIT BIT(3) -#define CHGR_CCMP_CFG 0xFA -#define JEITA_TEMP_HARD_LIMIT_BIT BIT(5) -#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4) -#define HVDCP_ADAPTER_SEL_9V_BIT BIT(4) -#define HVDCP_AUTH_ALG_EN_BIT BIT(6) -#define CMD_APSD 0x41 -#define APSD_RERUN_BIT BIT(0) -#define OTG_CFG 0xF1 -#define HICCUP_ENABLED_BIT BIT(6) -#define OTG_PIN_POLARITY_BIT BIT(4) -#define OTG_PIN_ACTIVE_LOW BIT(4) -#define OTG_EN_CTRL_MASK SMB_MASK(3, 2) -#define OTG_PIN_CTRL_RID_DIS 0x04 -#define OTG_CMD_CTRL_RID_EN 0x08 -#define AICL_ADC_BIT BIT(6) -static void batt_ov_wa_check(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - /* disable-'battery OV disables charging' feature */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2, - CHG_BAT_OV_ECC, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return; - } - - /* - * if battery OV is set: - * restart charging by disable/enable charging - */ - rc = smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read Battery RT status rc = %d\n", rc); - return; - } - - if (reg & BAT_OV_BIT) { - rc = smbchg_charging_en(chip, false); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't disable charging: rc = %d\n", rc); - return; - } - - /* delay for charging-disable to take affect */ - msleep(200); - - rc = smbchg_charging_en(chip, true); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't enable charging: rc = %d\n", rc); - return; - } - } -} - -static int smbchg_hw_init(struct smbchg_chip *chip) -{ - int rc, i; - u8 reg, mask; - - rc = smbchg_read(chip, chip->revision, - chip->misc_base + REVISION1_REG, 4); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read revision rc=%d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n", - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); - - /* Setup 9V HVDCP */ - if (!chip->hvdcp_not_supported) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) { - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", - rc); - return rc; - } - } - - if (chip->aicl_rerun_period_s > 0) { - rc = smbchg_set_aicl_rerun_period_s(chip, - chip->aicl_rerun_period_s); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n", - rc); - return rc; - } - } - - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG, - FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n", - rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0, - INPUT_MISSING_POLLER_EN_BIT, - INPUT_MISSING_POLLER_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n", - rc); - return rc; - } - - /* - * Do not force using current from the register i.e. use auto - * power source detect (APSD) mA ratings for the initial current values. - * - * If this is set, AICL will not rerun at 9V for HVDCPs - */ - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USE_REGISTER_FOR_CURRENT, 0); - - if (rc < 0) { - dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc); - return rc; - } - - /* - * set chg en by cmd register, set chg en by writing bit 1, - * enable auto pre to fast, enable auto recharge by default. - * enable current termination and charge inhibition based on - * the device tree configuration. - */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2, - CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN - | I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT, - CHG_EN_POLARITY_BIT - | (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0) - | (chip->iterm_disabled ? I_TERM_BIT : 0)); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return rc; - } - - /* - * enable battery charging to make sure it hasn't been changed earlier - * by the bootloader. - */ - rc = smbchg_charging_en(chip, true); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc); - return rc; - } - - /* - * Based on the configuration, use the analog sensors or the fuelgauge - * adc for recharge threshold source. - */ - - if (chip->chg_inhibit_source_fg) - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1, - TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, - TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT); - else - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1, - TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0); - - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return rc; - } - - /* - * control USB suspend via command bits and set correct 100/500mA - * polarity on the usb current - */ - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - USB51_COMMAND_POL | USB51AC_CTRL, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc); - return rc; - } - - check_battery_type(chip); - - /* set the float voltage */ - if (chip->vfloat_mv != -EINVAL) { - rc = smbchg_float_voltage_set(chip, chip->vfloat_mv); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set float voltage rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv); - } - - /* set the fast charge current compensation */ - if (chip->fastchg_current_comp != -EINVAL) { - rc = smbchg_fastchg_current_comp_set(chip, - chip->fastchg_current_comp); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "set fastchg current comp to %d\n", - chip->fastchg_current_comp); - } - - /* set the float voltage compensation */ - if (chip->float_voltage_comp != -EINVAL) { - rc = smbchg_float_voltage_comp_set(chip, - chip->float_voltage_comp); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "set float voltage comp to %d\n", - chip->float_voltage_comp); - } - - /* set iterm */ - if (chip->iterm_ma != -EINVAL) { - if (chip->iterm_disabled) { - dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n"); - return -EINVAL; - } else { - smbchg_iterm_set(chip, chip->iterm_ma); - } - } - - /* set the safety time voltage */ - if (chip->safety_time != -EINVAL) { - reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) | - (chip->prechg_safety_time > 0 - ? 0 : PRECHG_SFT_TIMER_DISABLE_BIT); - - for (i = 0; i < ARRAY_SIZE(chg_time); i++) { - if (chip->safety_time <= chg_time[i]) { - reg |= i << SAFETY_TIME_MINUTES_SHIFT; - break; - } - } - for (i = 0; i < ARRAY_SIZE(prechg_time); i++) { - if (chip->prechg_safety_time <= prechg_time[i]) { - reg |= i; - break; - } - } - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + SFT_CFG, - SFT_EN_MASK | SFT_TO_MASK | - (chip->prechg_safety_time > 0 - ? PRECHG_SFT_TO_MASK : 0), reg); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set safety timer rc = %d\n", - rc); - return rc; - } - chip->safety_timer_en = true; - } else { - rc = smbchg_read(chip, ®, chip->chgr_base + SFT_CFG, 1); - if (rc < 0) - dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n", - rc); - else if (!(reg & SFT_EN_MASK)) - chip->safety_timer_en = true; - } - - /* configure jeita temperature hard limit */ - if (chip->jeita_temp_hard_limit >= 0) { - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHGR_CCMP_CFG, - JEITA_TEMP_HARD_LIMIT_BIT, - chip->jeita_temp_hard_limit - ? 0 : JEITA_TEMP_HARD_LIMIT_BIT); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set jeita temp hard limit rc = %d\n", - rc); - return rc; - } - } - - /* make the buck switch faster to prevent some vbus oscillation */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + TR_8OR32B, - BUCK_8_16_FREQ_BIT, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc); - return rc; - } - - /* battery missing detection */ - mask = BATT_MISSING_ALGO_BIT; - reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT; - if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) { - mask |= BMD_PIN_SRC_MASK; - reg |= chip->bmd_pin_src << PIN_SRC_SHIFT; - } - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + BM_CFG, mask, reg); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set batt_missing config = %d\n", - rc); - return rc; - } - - if (chip->vchg_adc_channel != -EINVAL) { - /* configure and enable VCHG */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG, - VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT, - VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set recharge rc = %d\n", - rc); - return rc; - } - } - - smbchg_charging_status_change(chip); - - vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0); - vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0); - /* resume threshold */ - if (chip->resume_delta_mv != -EINVAL) { - - /* - * Configure only if the recharge threshold source is not - * fuel gauge ADC. - */ - if (!chip->chg_inhibit_source_fg) { - if (chip->resume_delta_mv < 100) - reg = CHG_INHIBIT_50MV_VAL; - else if (chip->resume_delta_mv < 200) - reg = CHG_INHIBIT_100MV_VAL; - else if (chip->resume_delta_mv < 300) - reg = CHG_INHIBIT_200MV_VAL; - else - reg = CHG_INHIBIT_300MV_VAL; - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHG_INHIB_CFG_REG, - CHG_INHIBIT_MASK, reg); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n", - rc); - return rc; - } - } - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHGR_CFG, - RCHG_LVL_BIT, - (chip->resume_delta_mv - < chip->tables.rchg_thr_mv) - ? 0 : RCHG_LVL_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set recharge rc = %d\n", - rc); - return rc; - } - } - - /* DC path current settings */ - if (chip->dc_psy_type != -EINVAL) { - rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true, - chip->dc_target_current_ma); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't vote for initial DC ICL rc=%d\n", rc); - return rc; - } - } - - - /* - * on some devices the battery is powered via external sources which - * could raise its voltage above the float voltage. smbchargers go - * in to reverse boost in such a situation and the workaround is to - * disable float voltage compensation (note that the battery will appear - * hot/cold when powered via external source). - */ - if (chip->soft_vfloat_comp_disabled) { - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC, - VFLOAT_COMP_ENABLE_MASK, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n", - rc); - return rc; - } - } - - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, - chip->cfg_fastchg_current_ma); - if (rc < 0) { - dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc); - return rc; - } - - rc = smbchg_read(chip, &chip->original_usbin_allowance, - chip->usb_chgpth_base + USBIN_CHGR_CFG, 1); - if (rc < 0) - dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc); - - if (chip->wipower_dyn_icl_avail) { - rc = smbchg_wipower_ilim_config(chip, - &(chip->wipower_default.entries[0])); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n", - rc); - return rc; - } - } - /* unsuspend dc path, it could be suspended by the bootloader */ - rc = smbchg_dc_suspend(chip, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc); - return rc; - } - - if (chip->force_aicl_rerun) { - /* vote to enable hw aicl */ - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - DEFAULT_CONFIG_HW_AICL_VOTER, true, 0); - if (rc < 0) { - pr_err("Couldn't vote enable hw aicl rerun rc=%d\n", - rc); - return rc; - } - } - - if (chip->schg_version == QPNP_SCHG_LITE) { - /* enable OTG hiccup mode */ - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG, - HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT); - if (rc < 0) - dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n", - rc); - } - - if (chip->otg_pinctrl) { - /* configure OTG enable to pin control active low */ - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG, - OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK, - OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n", - rc); - return rc; - } - } - - if (chip->wa_flags & SMBCHG_BATT_OV_WA) - batt_ov_wa_check(chip); - - /* turn off AICL adc for improved accuracy */ - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0); - if (rc) - pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", - rc); - - return rc; -} - -static struct of_device_id smbchg_match_table[] = { - { - .compatible = "qcom,qpnp-smbcharger", - }, - { }, -}; - -#define DC_MA_MIN 300 -#define DC_MA_MAX 2000 -#define OF_PROP_READ(chip, prop, dt_property, retval, optional) \ -do { \ - if (retval) \ - break; \ - if (optional) \ - prop = -EINVAL; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," dt_property , \ - &prop); \ - \ - if ((retval == -EINVAL) && optional) \ - retval = 0; \ - else if (retval) \ - dev_err(chip->dev, "Error reading " #dt_property \ - " property rc = %d\n", rc); \ -} while (0) - -#define ILIM_ENTRIES 3 -#define VOLTAGE_RANGE_ENTRIES 2 -#define RANGE_ENTRY (ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES) -static int smb_parse_wipower_map_dt(struct smbchg_chip *chip, - struct ilim_map *map, char *property) -{ - struct device_node *node = chip->dev->of_node; - int total_elements, size; - struct property *prop; - const __be32 *data; - int num, i; - - prop = of_find_property(node, property, &size); - if (!prop) { - dev_err(chip->dev, "%s missing\n", property); - return -EINVAL; - } - - total_elements = size / sizeof(int); - if (total_elements % RANGE_ENTRY) { - dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n", - property, RANGE_ENTRY, total_elements); - return -EINVAL; - } - - data = prop->value; - num = total_elements / RANGE_ENTRY; - map->entries = devm_kzalloc(chip->dev, - num * sizeof(struct ilim_entry), GFP_KERNEL); - if (!map->entries) { - dev_err(chip->dev, "kzalloc failed for default ilim\n"); - return -ENOMEM; - } - for (i = 0; i < num; i++) { - map->entries[i].vmin_uv = be32_to_cpup(data++); - map->entries[i].vmax_uv = be32_to_cpup(data++); - map->entries[i].icl_pt_ma = be32_to_cpup(data++); - map->entries[i].icl_lv_ma = be32_to_cpup(data++); - map->entries[i].icl_hv_ma = be32_to_cpup(data++); - } - map->num = num; - return 0; -} - -static int smb_parse_wipower_dt(struct smbchg_chip *chip) -{ - int rc = 0; - - chip->wipower_dyn_icl_avail = false; - - if (!chip->vadc_dev) - goto err; - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default, - "qcom,wipower-default-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n", - rc); - goto err; - } - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt, - "qcom,wipower-pt-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n", - rc); - goto err; - } - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2, - "qcom,wipower-div2-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n", - rc); - goto err; - } - chip->wipower_dyn_icl_avail = true; - return 0; -err: - chip->wipower_default.num = 0; - chip->wipower_pt.num = 0; - chip->wipower_default.num = 0; - if (chip->wipower_default.entries) - devm_kfree(chip->dev, chip->wipower_default.entries); - if (chip->wipower_pt.entries) - devm_kfree(chip->dev, chip->wipower_pt.entries); - if (chip->wipower_div2.entries) - devm_kfree(chip->dev, chip->wipower_div2.entries); - chip->wipower_default.entries = NULL; - chip->wipower_pt.entries = NULL; - chip->wipower_div2.entries = NULL; - chip->vadc_dev = NULL; - return rc; -} - -#define DEFAULT_VLED_MAX_UV 3500000 -#define DEFAULT_FCC_MA 2000 -static int smb_parse_dt(struct smbchg_chip *chip) -{ - int rc = 0, ocp_thresh = -EINVAL; - struct device_node *node = chip->dev->of_node; - const char *dc_psy_type, *bpd; - - if (!node) { - dev_err(chip->dev, "device tree info. missing\n"); - return -EINVAL; - } - - /* read optional u32 properties */ - OF_PROP_READ(chip, ocp_thresh, - "ibat-ocp-threshold-ua", rc, 1); - if (ocp_thresh >= 0) - smbchg_ibat_ocp_threshold_ua = ocp_thresh; - OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1); - OF_PROP_READ(chip, chip->cfg_fastchg_current_ma, - "fastchg-current-ma", rc, 1); - if (chip->cfg_fastchg_current_ma == -EINVAL) - chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA; - OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1); - OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1); - OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1); - if (chip->vled_max_uv < 0) - chip->vled_max_uv = DEFAULT_VLED_MAX_UV; - OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1); - if (chip->rpara_uohm < 0) - chip->rpara_uohm = 0; - OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins", - rc, 1); - OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp", - rc, 1); - OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp", - rc, 1); - if (chip->safety_time != -EINVAL && - (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) { - dev_err(chip->dev, "Bad charging-timeout-mins %d\n", - chip->safety_time); - return -EINVAL; - } - if (chip->prechg_safety_time != -EINVAL && - (chip->prechg_safety_time > - prechg_time[ARRAY_SIZE(prechg_time) - 1])) { - dev_err(chip->dev, "Bad precharging-timeout-mins %d\n", - chip->prechg_safety_time); - return -EINVAL; - } - OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1); - OF_PROP_READ(chip, chip->parallel.min_current_thr_ma, - "parallel-usb-min-current-ma", rc, 1); - OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma, - "parallel-usb-9v-min-current-ma", rc, 1); - OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma, - "parallel-allowed-lowering-ma", rc, 1); - if (chip->parallel.min_current_thr_ma != -EINVAL - && chip->parallel.min_9v_current_thr_ma != -EINVAL) - chip->parallel.avail = true; - /* - * use the dt values if they exist, otherwise do not touch the params - */ - of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent", - &smbchg_main_chg_fcc_percent); - of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent", - &smbchg_main_chg_icl_percent); - pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n", - chip->parallel.min_current_thr_ma, - chip->parallel.min_9v_current_thr_ma); - OF_PROP_READ(chip, chip->jeita_temp_hard_limit, - "jeita-temp-hard-limit", rc, 1); - OF_PROP_READ(chip, chip->aicl_rerun_period_s, - "aicl-rerun-period-s", rc, 1); - OF_PROP_READ(chip, chip->vchg_adc_channel, - "vchg-adc-channel-id", rc, 1); - - /* read boolean configuration properties */ - chip->use_vfloat_adjustments = of_property_read_bool(node, - "qcom,autoadjust-vfloat"); - chip->bmd_algo_disabled = of_property_read_bool(node, - "qcom,bmd-algo-disabled"); - chip->iterm_disabled = of_property_read_bool(node, - "qcom,iterm-disabled"); - chip->soft_vfloat_comp_disabled = of_property_read_bool(node, - "qcom,soft-vfloat-comp-disabled"); - chip->chg_enabled = !(of_property_read_bool(node, - "qcom,charging-disabled")); - chip->charge_unknown_battery = of_property_read_bool(node, - "qcom,charge-unknown-battery"); - chip->chg_inhibit_en = of_property_read_bool(node, - "qcom,chg-inhibit-en"); - chip->chg_inhibit_source_fg = of_property_read_bool(node, - "qcom,chg-inhibit-fg"); - chip->low_volt_dcin = of_property_read_bool(node, - "qcom,low-volt-dcin"); - chip->force_aicl_rerun = of_property_read_bool(node, - "qcom,force-aicl-rerun"); - chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node, - "qcom,skip-usb-suspend-for-fake-battery"); - - /* parse the battery missing detection pin source */ - rc = of_property_read_string(chip->pdev->dev.of_node, - "qcom,bmd-pin-src", &bpd); - if (rc) { - /* Select BAT_THM as default BPD scheme */ - chip->bmd_pin_src = BPD_TYPE_DEFAULT; - rc = 0; - } else { - chip->bmd_pin_src = get_bpd(bpd); - if (chip->bmd_pin_src < 0) { - dev_err(chip->dev, - "failed to determine bpd schema %d\n", rc); - return rc; - } - } - - /* parse the dc power supply configuration */ - rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type); - if (rc) { - chip->dc_psy_type = -EINVAL; - rc = 0; - } else { - if (strcmp(dc_psy_type, "Mains") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS; - else if (strcmp(dc_psy_type, "Wireless") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS; - else if (strcmp(dc_psy_type, "Wipower") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER; - } - if (chip->dc_psy_type != -EINVAL) { - OF_PROP_READ(chip, chip->dc_target_current_ma, - "dc-psy-ma", rc, 0); - if (rc) - return rc; - if (chip->dc_target_current_ma < DC_MA_MIN - || chip->dc_target_current_ma > DC_MA_MAX) { - dev_err(chip->dev, "Bad dc mA %d\n", - chip->dc_target_current_ma); - return -EINVAL; - } - } - - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) - smb_parse_wipower_dt(chip); - - /* read the bms power supply name */ - rc = of_property_read_string(node, "qcom,bms-psy-name", - &chip->bms_psy_name); - if (rc) - chip->bms_psy_name = NULL; - - /* read the battery power supply name */ - rc = of_property_read_string(node, "qcom,battery-psy-name", - &chip->battery_psy_name); - if (rc) - chip->battery_psy_name = "battery"; - - /* Get the charger led support property */ - chip->cfg_chg_led_sw_ctrl = - of_property_read_bool(node, "qcom,chg-led-sw-controls"); - chip->cfg_chg_led_support = - of_property_read_bool(node, "qcom,chg-led-support"); - - if (of_find_property(node, "qcom,thermal-mitigation", - &chip->thermal_levels)) { - chip->thermal_mitigation = devm_kzalloc(chip->dev, - chip->thermal_levels, - GFP_KERNEL); - - if (chip->thermal_mitigation == NULL) { - dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n"); - return -ENOMEM; - } - - chip->thermal_levels /= sizeof(int); - rc = of_property_read_u32_array(node, - "qcom,thermal-mitigation", - chip->thermal_mitigation, chip->thermal_levels); - if (rc) { - dev_err(chip->dev, - "Couldn't read threm limits rc = %d\n", rc); - return rc; - } - } - - chip->skip_usb_notification - = of_property_read_bool(node, - "qcom,skip-usb-notification"); - - chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl"); - - return 0; -} - -#define SUBTYPE_REG 0x5 -#define SMBCHG_CHGR_SUBTYPE 0x1 -#define SMBCHG_OTG_SUBTYPE 0x8 -#define SMBCHG_BAT_IF_SUBTYPE 0x3 -#define SMBCHG_USB_CHGPTH_SUBTYPE 0x4 -#define SMBCHG_DC_CHGPTH_SUBTYPE 0x5 -#define SMBCHG_MISC_SUBTYPE 0x7 -#define SMBCHG_LITE_CHGR_SUBTYPE 0x51 -#define SMBCHG_LITE_OTG_SUBTYPE 0x58 -#define SMBCHG_LITE_BAT_IF_SUBTYPE 0x53 -#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54 -#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55 -#define SMBCHG_LITE_MISC_SUBTYPE 0x57 -static int smbchg_request_irq(struct smbchg_chip *chip, - struct device_node *child, - int irq_num, char *irq_name, - irqreturn_t (irq_handler)(int irq, void *_chip), - int flags) -{ - int rc; - - irq_num = of_irq_get_byname(child, irq_name); - if (irq_num < 0) { - dev_err(chip->dev, "Unable to get %s irqn", irq_name); - rc = -ENXIO; - } - rc = devm_request_threaded_irq(chip->dev, - irq_num, NULL, irq_handler, flags, irq_name, - chip); - if (rc < 0) { - dev_err(chip->dev, "Unable to request %s irq: %dn", - irq_name, rc); - rc = -ENXIO; - } - return 0; -} - -static int smbchg_request_irqs(struct smbchg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - | IRQF_ONESHOT; - - if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(chip->pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - rc = 0; - continue; - } - - rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1); - if (rc) { - dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n", - rc); - return rc; - } - - switch (subtype) { - case SMBCHG_CHGR_SUBTYPE: - case SMBCHG_LITE_CHGR_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->chg_error_irq, "chg-error", - chg_error_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->taper_irq, - "chg-taper-thr", taper_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - disable_irq_nosync(chip->taper_irq); - rc = smbchg_request_irq(chip, child, chip->chg_term_irq, - "chg-tcc-thr", chg_term_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->recharge_irq, - "chg-rechg-thr", recharge_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->fastchg_irq, - "chg-p2f-thr", fastchg_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->chg_term_irq); - enable_irq_wake(chip->chg_error_irq); - enable_irq_wake(chip->fastchg_irq); - break; - case SMBCHG_BAT_IF_SUBTYPE: - case SMBCHG_LITE_BAT_IF_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->batt_hot_irq, - "batt-hot", batt_hot_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_warm_irq, - "batt-warm", batt_warm_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_cool_irq, - "batt-cool", batt_cool_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_cold_irq, - "batt-cold", batt_cold_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_missing_irq, - "batt-missing", batt_pres_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->vbat_low_irq, - "batt-low", vbat_low_handler, flags); - if (rc < 0) - return rc; - - enable_irq_wake(chip->batt_hot_irq); - enable_irq_wake(chip->batt_warm_irq); - enable_irq_wake(chip->batt_cool_irq); - enable_irq_wake(chip->batt_cold_irq); - enable_irq_wake(chip->batt_missing_irq); - enable_irq_wake(chip->vbat_low_irq); - break; - case SMBCHG_USB_CHGPTH_SUBTYPE: - case SMBCHG_LITE_USB_CHGPTH_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->usbin_uv_irq, - "usbin-uv", usbin_uv_handler, - flags | IRQF_EARLY_RESUME); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->usbin_ov_irq, - "usbin-ov", usbin_ov_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->src_detect_irq, - "usbin-src-det", - src_detect_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->aicl_done_irq, - "aicl-done", - aicl_done_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - - if (chip->schg_version != QPNP_SCHG_LITE) { - rc = smbchg_request_irq(chip, child, - chip->otg_fail_irq, "otg-fail", - otg_fail_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_oc_irq, "otg-oc", - otg_oc_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->usbid_change_irq, "usbid-change", - usbid_change_handler, - (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - enable_irq_wake(chip->otg_oc_irq); - enable_irq_wake(chip->usbid_change_irq); - enable_irq_wake(chip->otg_fail_irq); - } - enable_irq_wake(chip->usbin_uv_irq); - enable_irq_wake(chip->usbin_ov_irq); - enable_irq_wake(chip->src_detect_irq); - if (chip->parallel.avail && chip->usb_present) { - rc = enable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = true; - } - break; - case SMBCHG_DC_CHGPTH_SUBTYPE: - case SMBCHG_LITE_DC_CHGPTH_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq, - "dcin-uv", dcin_uv_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->dcin_uv_irq); - break; - case SMBCHG_MISC_SUBTYPE: - case SMBCHG_LITE_MISC_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->power_ok_irq, - "power-ok", power_ok_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->chg_hot_irq, - "temp-shutdown", chg_hot_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->wdog_timeout_irq, - "wdog-timeout", - wdog_timeout_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->chg_hot_irq); - enable_irq_wake(chip->wdog_timeout_irq); - break; - case SMBCHG_OTG_SUBTYPE: - break; - case SMBCHG_LITE_OTG_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->usbid_change_irq, "usbid-change", - usbid_change_handler, - (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_oc_irq, "otg-oc", - otg_oc_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_fail_irq, "otg-fail", - otg_fail_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->usbid_change_irq); - enable_irq_wake(chip->otg_oc_irq); - enable_irq_wake(chip->otg_fail_irq); - break; - } - } - - return rc; -} - -#define REQUIRE_BASE(chip, base, rc) \ -do { \ - if (!rc && !chip->base) { \ - dev_err(chip->dev, "Missing " #base "\n"); \ - rc = -EINVAL; \ - } \ -} while (0) - -static int smbchg_parse_peripherals(struct smbchg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - - if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(chip->pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - rc = 0; - continue; - } - - rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1); - if (rc) { - dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n", - rc); - return rc; - } - - switch (subtype) { - case SMBCHG_CHGR_SUBTYPE: - case SMBCHG_LITE_CHGR_SUBTYPE: - chip->chgr_base = base; - break; - case SMBCHG_BAT_IF_SUBTYPE: - case SMBCHG_LITE_BAT_IF_SUBTYPE: - chip->bat_if_base = base; - break; - case SMBCHG_USB_CHGPTH_SUBTYPE: - case SMBCHG_LITE_USB_CHGPTH_SUBTYPE: - chip->usb_chgpth_base = base; - break; - case SMBCHG_DC_CHGPTH_SUBTYPE: - case SMBCHG_LITE_DC_CHGPTH_SUBTYPE: - chip->dc_chgpth_base = base; - break; - case SMBCHG_MISC_SUBTYPE: - case SMBCHG_LITE_MISC_SUBTYPE: - chip->misc_base = base; - break; - case SMBCHG_OTG_SUBTYPE: - case SMBCHG_LITE_OTG_SUBTYPE: - chip->otg_base = base; - break; - } - } - - REQUIRE_BASE(chip, chgr_base, rc); - REQUIRE_BASE(chip, bat_if_base, rc); - REQUIRE_BASE(chip, usb_chgpth_base, rc); - REQUIRE_BASE(chip, dc_chgpth_base, rc); - REQUIRE_BASE(chip, misc_base, rc); - - return rc; -} - -static inline void dump_reg(struct smbchg_chip *chip, u16 addr, - const char *name) -{ - u8 reg; - - smbchg_read(chip, ®, addr, 1); - pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg); -} - -/* dumps useful registers for debug */ -static void dump_regs(struct smbchg_chip *chip) -{ - u16 addr; - - /* charger peripheral */ - for (addr = 0xB; addr <= 0x10; addr++) - dump_reg(chip, chip->chgr_base + addr, "CHGR Status"); - for (addr = 0xF0; addr <= 0xFF; addr++) - dump_reg(chip, chip->chgr_base + addr, "CHGR Config"); - /* battery interface peripheral */ - dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status"); - dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command"); - for (addr = 0xF0; addr <= 0xFB; addr++) - dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config"); - /* usb charge path peripheral */ - for (addr = 0x7; addr <= 0x10; addr++) - dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status"); - dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command"); - for (addr = 0xF0; addr <= 0xF5; addr++) - dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config"); - /* dc charge path peripheral */ - dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status"); - for (addr = 0xF0; addr <= 0xF6; addr++) - dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config"); - /* misc peripheral */ - dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status"); - dump_reg(chip, chip->misc_base + RT_STS, "MISC Status"); - for (addr = 0xF0; addr <= 0xF3; addr++) - dump_reg(chip, chip->misc_base + addr, "MISC CFG"); -} - -static int create_debugfs_entries(struct smbchg_chip *chip) -{ - struct dentry *ent; - - chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL); - if (!chip->debug_root) { - dev_err(chip->dev, "Couldn't create debug dir\n"); - return -EINVAL; - } - - ent = debugfs_create_file("force_dcin_icl_check", - S_IFREG | S_IWUSR | S_IRUGO, - chip->debug_root, chip, - &force_dcin_icl_ops); - if (!ent) { - dev_err(chip->dev, - "Couldn't create force dcin icl check file\n"); - return -EINVAL; - } - return 0; -} - -static int smbchg_check_chg_version(struct smbchg_chip *chip) -{ - struct pmic_revid_data *pmic_rev_id; - struct device_node *revid_dev_node; - int rc; - - revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR(pmic_rev_id)) { - rc = PTR_ERR(revid_dev_node); - if (rc != -EPROBE_DEFER) - pr_err("Unable to get pmic_revid rc=%d\n", rc); - return rc; - } - - switch (pmic_rev_id->pmic_subtype) { - case PMI8994: - chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA - | SMBCHG_BATT_OV_WA - | SMBCHG_CC_ESR_WA - | SMBCHG_RESTART_WA; - use_pmi8994_tables(chip); - chip->schg_version = QPNP_SCHG; - break; - case PMI8950: - case PMI8937: - chip->wa_flags |= SMBCHG_BATT_OV_WA; - if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ { - chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA; - } else { /* rev > PMI8950 v1.0 */ - chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA - | SMBCHG_USB100_WA; - } - use_pmi8994_tables(chip); - chip->tables.aicl_rerun_period_table = - aicl_rerun_period_schg_lite; - chip->tables.aicl_rerun_period_len = - ARRAY_SIZE(aicl_rerun_period_schg_lite); - - chip->schg_version = QPNP_SCHG_LITE; - if (pmic_rev_id->pmic_subtype == PMI8937) - chip->hvdcp_not_supported = true; - break; - case PMI8996: - chip->wa_flags |= SMBCHG_CC_ESR_WA - | SMBCHG_FLASH_ICL_DISABLE_WA - | SMBCHG_RESTART_WA - | SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA; - use_pmi8996_tables(chip); - chip->schg_version = QPNP_SCHG; - break; - default: - pr_err("PMIC subtype %d not supported, WA flags not set\n", - pmic_rev_id->pmic_subtype); - } - - pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n", - pmic_rev_id->pmic_name, chip->wa_flags, - chip->hvdcp_not_supported ? "false" : "true"); - - return 0; -} - -static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip) -{ - enum power_supply_type usb_supply_type; - char *usb_type_name; - int rc; - - if (!(chip->wa_flags & SMBCHG_RESTART_WA)) - return; - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP - && !is_hvdcp_present(chip)) { - pr_smb(PR_STATUS, "DCP found rerunning APSD\n"); - rc = vote(chip->usb_icl_votable, - CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300); - if (rc < 0) - pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n", - rc); - - pr_smb(PR_STATUS, "Faking Removal\n"); - fake_insertion_removal(chip, false); - msleep(500); - pr_smb(PR_STATUS, "Faking Insertion\n"); - fake_insertion_removal(chip, true); - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) { - msleep(500); - pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n"); - fake_insertion_removal(chip, false); - msleep(500); - pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n"); - fake_insertion_removal(chip, true); - } - - rc = vote(chip->usb_icl_votable, - CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n", - rc); - } -} - -static int smbchg_probe(struct platform_device *pdev) -{ - int rc; - struct smbchg_chip *chip; - struct power_supply *typec_psy = NULL; - struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev; - const char *typec_psy_name; - struct power_supply_config usb_psy_cfg = {}; - struct power_supply_config batt_psy_cfg = {}; - struct power_supply_config dc_psy_cfg = {}; - - if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) { - /* read the type power supply name */ - rc = of_property_read_string(pdev->dev.of_node, - "qcom,typec-psy-name", &typec_psy_name); - if (rc) { - pr_err("failed to get prop typec-psy-name rc=%d\n", - rc); - return rc; - } - - typec_psy = power_supply_get_by_name(typec_psy_name); - if (!typec_psy) { - pr_smb(PR_STATUS, - "Type-C supply not found, deferring probe\n"); - return -EPROBE_DEFER; - } - } - - vadc_dev = NULL; - if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) { - vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin"); - if (IS_ERR(vadc_dev)) { - rc = PTR_ERR(vadc_dev); - if (rc != -EPROBE_DEFER) - dev_err(&pdev->dev, - "Couldn't get vadc rc=%d\n", - rc); - return rc; - } - } - - vchg_vadc_dev = NULL; - if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) { - vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns"); - if (IS_ERR(vchg_vadc_dev)) { - rc = PTR_ERR(vchg_vadc_dev); - if (rc != -EPROBE_DEFER) - dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n", - rc); - return rc; - } - } - - - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!chip->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); - return -EINVAL; - } - - chip->fcc_votable = create_votable("BATT_FCC", - VOTE_MIN, - set_fastchg_current_vote_cb, chip); - if (IS_ERR(chip->fcc_votable)) { - rc = PTR_ERR(chip->fcc_votable); - goto votables_cleanup; - } - - chip->usb_icl_votable = create_votable("USB_ICL", - VOTE_MIN, - set_usb_current_limit_vote_cb, chip); - if (IS_ERR(chip->usb_icl_votable)) { - rc = PTR_ERR(chip->usb_icl_votable); - goto votables_cleanup; - } - - chip->dc_icl_votable = create_votable("DCIN_ICL", - VOTE_MIN, - set_dc_current_limit_vote_cb, chip); - if (IS_ERR(chip->dc_icl_votable)) { - rc = PTR_ERR(chip->dc_icl_votable); - goto votables_cleanup; - } - - chip->usb_suspend_votable = create_votable("USB_SUSPEND", - VOTE_SET_ANY, - usb_suspend_vote_cb, chip); - if (IS_ERR(chip->usb_suspend_votable)) { - rc = PTR_ERR(chip->usb_suspend_votable); - goto votables_cleanup; - } - - chip->dc_suspend_votable = create_votable("DC_SUSPEND", - VOTE_SET_ANY, - dc_suspend_vote_cb, chip); - if (IS_ERR(chip->dc_suspend_votable)) { - rc = PTR_ERR(chip->dc_suspend_votable); - goto votables_cleanup; - } - - chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND", - VOTE_SET_ANY, - charging_suspend_vote_cb, chip); - if (IS_ERR(chip->battchg_suspend_votable)) { - rc = PTR_ERR(chip->battchg_suspend_votable); - goto votables_cleanup; - } - - chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE", - VOTE_SET_ANY, - smbchg_hw_aicl_rerun_disable_cb, chip); - if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) { - rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable); - goto votables_cleanup; - } - - chip->hw_aicl_rerun_enable_indirect_votable = create_votable( - "HWAICL_ENABLE_INDIRECT", - VOTE_SET_ANY, - smbchg_hw_aicl_rerun_enable_indirect_cb, chip); - if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) { - rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable); - goto votables_cleanup; - } - - chip->aicl_deglitch_short_votable = create_votable( - "HWAICL_SHORT_DEGLITCH", - VOTE_SET_ANY, - smbchg_aicl_deglitch_config_cb, chip); - if (IS_ERR(chip->aicl_deglitch_short_votable)) { - rc = PTR_ERR(chip->aicl_deglitch_short_votable); - goto votables_cleanup; - } - - INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work); - INIT_DELAYED_WORK(&chip->parallel_en_work, - smbchg_parallel_usb_en_work); - INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work); - INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work); - init_completion(&chip->src_det_lowered); - init_completion(&chip->src_det_raised); - init_completion(&chip->usbin_uv_lowered); - init_completion(&chip->usbin_uv_raised); - chip->vadc_dev = vadc_dev; - chip->vchg_vadc_dev = vchg_vadc_dev; - chip->pdev = pdev; - chip->dev = &pdev->dev; - - chip->typec_psy = typec_psy; - chip->fake_battery_soc = -EINVAL; - chip->usb_online = -EINVAL; - dev_set_drvdata(&pdev->dev, chip); - - spin_lock_init(&chip->sec_access_lock); - mutex_init(&chip->therm_lvl_lock); - mutex_init(&chip->usb_set_online_lock); - mutex_init(&chip->parallel.lock); - mutex_init(&chip->taper_irq_lock); - mutex_init(&chip->pm_lock); - mutex_init(&chip->wipower_config); - mutex_init(&chip->usb_status_lock); - device_init_wakeup(chip->dev, true); - - rc = smbchg_parse_peripherals(chip); - if (rc) { - dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc); - goto votables_cleanup; - } - - rc = smbchg_check_chg_version(chip); - if (rc) { - pr_err("Unable to check schg version rc=%d\n", rc); - goto votables_cleanup; - } - - rc = smb_parse_dt(chip); - if (rc < 0) { - dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc); - goto votables_cleanup; - } - - rc = smbchg_regulator_init(chip); - if (rc) { - dev_err(&pdev->dev, - "Couldn't initialize regulator rc=%d\n", rc); - goto votables_cleanup; - } - - chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable); - if (IS_ERR(chip->extcon)) { - dev_err(chip->dev, "failed to allocate extcon device\n"); - rc = PTR_ERR(chip->extcon); - goto votables_cleanup; - } - - rc = devm_extcon_dev_register(chip->dev, chip->extcon); - if (rc) { - dev_err(chip->dev, "failed to register extcon device\n"); - goto votables_cleanup; - } - - chip->usb_psy_d.name = "usb"; - chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB; - chip->usb_psy_d.get_property = smbchg_usb_get_property; - chip->usb_psy_d.set_property = smbchg_usb_set_property; - chip->usb_psy_d.properties = smbchg_usb_properties; - chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties); - chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable; - - usb_psy_cfg.drv_data = chip; - usb_psy_cfg.supplied_to = smbchg_usb_supplicants; - usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants); - - chip->usb_psy = devm_power_supply_register(chip->dev, - &chip->usb_psy_d, &usb_psy_cfg); - if (IS_ERR(chip->usb_psy)) { - dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n", - PTR_ERR(chip->usb_psy)); - rc = PTR_ERR(chip->usb_psy); - goto votables_cleanup; - } - - if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) { - chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm"); - if (IS_ERR(chip->dpdm_reg)) { - rc = PTR_ERR(chip->dpdm_reg); - goto votables_cleanup; - } - } - - rc = smbchg_hw_init(chip); - if (rc < 0) { - dev_err(&pdev->dev, - "Unable to intialize hardware rc = %d\n", rc); - goto out; - } - - rc = determine_initial_status(chip); - if (rc < 0) { - dev_err(&pdev->dev, - "Unable to determine init status rc = %d\n", rc); - goto out; - } - - chip->previous_soc = -EINVAL; - chip->batt_psy_d.name = chip->battery_psy_name; - chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY; - chip->batt_psy_d.get_property = smbchg_battery_get_property; - chip->batt_psy_d.set_property = smbchg_battery_set_property; - chip->batt_psy_d.properties = smbchg_battery_properties; - chip->batt_psy_d.num_properties = ARRAY_SIZE(smbchg_battery_properties); - chip->batt_psy_d.external_power_changed = smbchg_external_power_changed; - chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable; - - batt_psy_cfg.drv_data = chip; - batt_psy_cfg.num_supplicants = 0; - chip->batt_psy = devm_power_supply_register(chip->dev, - &chip->batt_psy_d, - &batt_psy_cfg); - if (IS_ERR(chip->batt_psy)) { - dev_err(&pdev->dev, - "Unable to register batt_psy rc = %ld\n", - PTR_ERR(chip->batt_psy)); - goto out; - } - - if (chip->dc_psy_type != -EINVAL) { - chip->dc_psy_d.name = "dc"; - chip->dc_psy_d.type = chip->dc_psy_type; - chip->dc_psy_d.get_property = smbchg_dc_get_property; - chip->dc_psy_d.set_property = smbchg_dc_set_property; - chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable; - chip->dc_psy_d.properties = smbchg_dc_properties; - chip->dc_psy_d.num_properties - = ARRAY_SIZE(smbchg_dc_properties); - - dc_psy_cfg.drv_data = chip; - dc_psy_cfg.num_supplicants - = ARRAY_SIZE(smbchg_dc_supplicants); - dc_psy_cfg.supplied_to = smbchg_dc_supplicants; - - chip->dc_psy = devm_power_supply_register(chip->dev, - &chip->dc_psy_d, - &dc_psy_cfg); - if (IS_ERR(chip->dc_psy)) { - dev_err(&pdev->dev, - "Unable to register dc_psy rc = %ld\n", - PTR_ERR(chip->dc_psy)); - goto out; - } - } - - if (chip->cfg_chg_led_support && - chip->schg_version == QPNP_SCHG_LITE) { - rc = smbchg_register_chg_led(chip); - if (rc) { - dev_err(chip->dev, - "Unable to register charger led: %d\n", - rc); - goto out; - } - - rc = smbchg_chg_led_controls(chip); - if (rc) { - dev_err(chip->dev, - "Failed to set charger led controld bit: %d\n", - rc); - goto unregister_led_class; - } - } - - rc = smbchg_request_irqs(chip); - if (rc < 0) { - dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc); - goto unregister_led_class; - } - - rerun_hvdcp_det_if_necessary(chip); - - dump_regs(chip); - create_debugfs_entries(chip); - dev_info(chip->dev, - "SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n", - version_str[chip->schg_version], - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR], - get_prop_batt_present(chip), - chip->dc_present, chip->usb_present); - return 0; - -unregister_led_class: - if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE) - led_classdev_unregister(&chip->led_cdev); -out: - handle_usb_removal(chip); -votables_cleanup: - if (chip->aicl_deglitch_short_votable) - destroy_votable(chip->aicl_deglitch_short_votable); - if (chip->hw_aicl_rerun_enable_indirect_votable) - destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable); - if (chip->hw_aicl_rerun_disable_votable) - destroy_votable(chip->hw_aicl_rerun_disable_votable); - if (chip->battchg_suspend_votable) - destroy_votable(chip->battchg_suspend_votable); - if (chip->dc_suspend_votable) - destroy_votable(chip->dc_suspend_votable); - if (chip->usb_suspend_votable) - destroy_votable(chip->usb_suspend_votable); - if (chip->dc_icl_votable) - destroy_votable(chip->dc_icl_votable); - if (chip->usb_icl_votable) - destroy_votable(chip->usb_icl_votable); - if (chip->fcc_votable) - destroy_votable(chip->fcc_votable); - return rc; -} - -static int smbchg_remove(struct platform_device *pdev) -{ - struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev); - - debugfs_remove_recursive(chip->debug_root); - - destroy_votable(chip->aicl_deglitch_short_votable); - destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable); - destroy_votable(chip->hw_aicl_rerun_disable_votable); - destroy_votable(chip->battchg_suspend_votable); - destroy_votable(chip->dc_suspend_votable); - destroy_votable(chip->usb_suspend_votable); - destroy_votable(chip->dc_icl_votable); - destroy_votable(chip->usb_icl_votable); - destroy_votable(chip->fcc_votable); - - return 0; -} - -static void smbchg_shutdown(struct platform_device *pdev) -{ - struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev); - int rc; - - if (!(chip->wa_flags & SMBCHG_RESTART_WA)) - return; - - if (!is_hvdcp_present(chip)) - return; - - pr_smb(PR_MISC, "Disable Parallel\n"); - mutex_lock(&chip->parallel.lock); - smbchg_parallel_en = 0; - smbchg_parallel_usb_disable(chip); - mutex_unlock(&chip->parallel.lock); - - pr_smb(PR_MISC, "Disable all interrupts\n"); - disable_irq(chip->aicl_done_irq); - disable_irq(chip->batt_cold_irq); - disable_irq(chip->batt_cool_irq); - disable_irq(chip->batt_hot_irq); - disable_irq(chip->batt_missing_irq); - disable_irq(chip->batt_warm_irq); - disable_irq(chip->chg_error_irq); - disable_irq(chip->chg_hot_irq); - disable_irq(chip->chg_term_irq); - disable_irq(chip->dcin_uv_irq); - disable_irq(chip->fastchg_irq); - disable_irq(chip->otg_fail_irq); - disable_irq(chip->otg_oc_irq); - disable_irq(chip->power_ok_irq); - disable_irq(chip->recharge_irq); - disable_irq(chip->src_detect_irq); - disable_irq(chip->taper_irq); - disable_irq(chip->usbid_change_irq); - disable_irq(chip->usbin_ov_irq); - disable_irq(chip->usbin_uv_irq); - disable_irq(chip->vbat_low_irq); - disable_irq(chip->wdog_timeout_irq); - - /* remove all votes for short deglitch */ - vote(chip->aicl_deglitch_short_votable, - VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0); - vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, false, 0); - - /* vote to ensure AICL rerun is enabled */ - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - SHUTDOWN_WORKAROUND_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't vote to enable indirect AICL rerun\n"); - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't vote to enable AICL rerun\n"); - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - return; - } - - pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n"); - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - return; - } - - /* disable HVDCP */ - pr_smb(PR_MISC, "Disable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) - pr_err("Couldn't disable HVDCP rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = true; - /* fake a removal */ - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) - pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc); - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) - pr_err("Couldn't fake insertion rc=%d\n", rc); - - pr_smb(PR_MISC, "Wait 1S to settle\n"); - msleep(1000); - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_STATUS, "wrote power off configurations\n"); -} - -static const struct dev_pm_ops smbchg_pm_ops = { -}; - -MODULE_DEVICE_TABLE(spmi, smbchg_id); - -static struct platform_driver smbchg_driver = { - .driver = { - .name = "qpnp-smbcharger", - .owner = THIS_MODULE, - .of_match_table = smbchg_match_table, - .pm = &smbchg_pm_ops, - }, - .probe = smbchg_probe, - .remove = smbchg_remove, - .shutdown = smbchg_shutdown, -}; - -static int __init smbchg_init(void) -{ - return platform_driver_register(&smbchg_driver); -} - -static void __exit smbchg_exit(void) -{ - return platform_driver_unregister(&smbchg_driver); -} - -module_init(smbchg_init); -module_exit(smbchg_exit); - -MODULE_DESCRIPTION("QPNP SMB Charger"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:qpnp-smbcharger"); -- cgit v1.2.3