summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-03-01 11:09:01 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-03-01 11:09:00 -0800
commit7fb2e5cac6c91817197ce431a8c57d06d5c35def (patch)
treedf9f550868f63ce6d8d8a185d85e1e7b1c605e51
parent72bcf7d23a93a2caaee8ab0654519af0b02c2a11 (diff)
parent941bfaf2dc352b2ba2cd7e50c55843e4585aaffb (diff)
Merge "power: supply: qcom: remove unused drivers"
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt275
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt394
-rw-r--r--arch/arm64/configs/msm-perf_defconfig2
-rw-r--r--arch/arm64/configs/msm_defconfig2
-rw-r--r--drivers/power/supply/qcom/Kconfig18
-rw-r--r--drivers/power/supply/qcom/Makefile2
-rw-r--r--drivers/power/supply/qcom/qpnp-fg.c7051
-rw-r--r--drivers/power/supply/qcom/qpnp-smbcharger.c8472
8 files changed, 0 insertions, 16216 deletions
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
- <slave-id peripheral-id interrupt-number>.
-- 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
- <slave-id peripheral-id interrupt-number>.
-- 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 <linux/atomic.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/regmap.h>
-#include <linux/of.h>
-#include <linux/rtc.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/init.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/of_irq.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/ktime.h>
-#include <linux/power_supply.h>
-#include <linux/of_batterydata.h>
-#include <linux/string_helpers.h>
-#include <linux/alarmtimer.h>
-#include <linux/qpnp/qpnp-revid.h>
-
-/* 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(&current_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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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(&current_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 <linux/regmap.h>
-#include <linux/spinlock.h>
-#include <linux/gpio.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/power_supply.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_irq.h>
-#include <linux/bitops.h>
-#include <linux/regulator/consumer.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/regulator/machine.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/printk.h>
-#include <linux/ratelimit.h>
-#include <linux/debugfs.h>
-#include <linux/leds.h>
-#include <linux/rtc.h>
-#include <linux/qpnp/qpnp-adc.h>
-#include <linux/batterydata-lib.h>
-#include <linux/of_batterydata.h>
-#include <linux/msm_bcl.h>
-#include <linux/ktime.h>
-#include <linux/extcon.h>
-#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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg,
- 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, &reg,
- 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, &reg,
- 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, &reg,
- 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg_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, &reg, 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, &reg, 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, &reg,
- 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, &reg, 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, &reg,
- 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, &reg,
- 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg,
- 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, &reg, 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, &reg, 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, &reg, 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");