spmi_devices: change to platform_devices
Change to using upstream spmi bus architecture. All the spmi devices, marked by spmi-dev-container, become platform devices. spmi-slave-container devices become spmi_devices each representing a slave. The read/write functions use regmap api's instead of calls to spmi_ext_register_read/write() implemented by the spmi bus. This regmap is instantiated per slave. The spmi bus helper functions like spmi_get_irq get changed to their platform bus equivalents. Change Kconfig files include * Remove dependence on OF_SPMI, MSM_QPNP_INT * There were few places where an earlier commit dcc2aedc80746acee589e4b47d3e6adf5d3ec253 missed adding dependence on SPMI along with MSM_SPMI. Fix them. * Add depends on ARCH_MSM. ARCH_MSM is used for internal builds. Change the nodes in DTSI files to confirm to the modified drivers. Update their binding docs to drop spmi-dev-container and spmi-slave-container; Finally update defconfig to use upstream SPMI. Change-Id: Ic85bff27c09c84b152cb38acbc3cadd05c0ec57a Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
This commit is contained in:
parent
065fd87ad3
commit
0c98f90e2e
46 changed files with 7620 additions and 2394 deletions
|
@ -1,7 +1,7 @@
|
||||||
* msm-qpnp-pin
|
* msm-qpnp-pin
|
||||||
|
|
||||||
msm-qpnp-pin is a GPIO chip driver for the MSM SPMI implementation.
|
msm-qpnp-pin is a GPIO chip driver for the MSM SPMI implementation.
|
||||||
It creates a spmi_device for every spmi-dev-container block of device_nodes.
|
It creates a platform_device for every block of device_nodes.
|
||||||
These device_nodes contained within specify the PMIC pin number associated
|
These device_nodes contained within specify the PMIC pin number associated
|
||||||
with each gpio chip. The driver will map these to Linux GPIO numbers.
|
with each gpio chip. The driver will map these to Linux GPIO numbers.
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@ with each gpio chip. The driver will map these to Linux GPIO numbers.
|
||||||
-Root Node-
|
-Root Node-
|
||||||
|
|
||||||
Required properties :
|
Required properties :
|
||||||
- spmi-dev-container : Used to specify the following child nodes as part of the
|
|
||||||
same SPMI device.
|
|
||||||
- gpio-controller : Specify as gpio-contoller. All child nodes will belong to
|
- gpio-controller : Specify as gpio-contoller. All child nodes will belong to
|
||||||
this gpio_chip.
|
this gpio_chip.
|
||||||
- #gpio-cells: We encode a PMIC pin number and a 32-bit flag field to
|
- #gpio-cells: We encode a PMIC pin number and a 32-bit flag field to
|
||||||
|
@ -188,13 +186,11 @@ qpnp: qcom,spmi@fc4c0000 {
|
||||||
#interrupt-cells = <3>;
|
#interrupt-cells = <3>;
|
||||||
|
|
||||||
qcom,pm8941@0 {
|
qcom,pm8941@0 {
|
||||||
spmi-slave-container;
|
|
||||||
reg = <0x0>;
|
reg = <0x0>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <1>;
|
#size-cells = <1>;
|
||||||
|
|
||||||
pm8941_gpios: gpios {
|
pm8941_gpios: gpios {
|
||||||
spmi-dev-container;
|
|
||||||
compatible = "qcom,qpnp-pin";
|
compatible = "qcom,qpnp-pin";
|
||||||
gpio-controller;
|
gpio-controller;
|
||||||
#gpio-cells = <2>;
|
#gpio-cells = <2>;
|
||||||
|
|
350
Documentation/devicetree/bindings/power/qpnp-charger.txt
Normal file
350
Documentation/devicetree/bindings/power/qpnp-charger.txt
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
Qualcomm QPNP Charger
|
||||||
|
|
||||||
|
The charger supports the switch mode battery charger and boost (SMBB)
|
||||||
|
peripherals on Qualcomm PMIC chips.
|
||||||
|
|
||||||
|
There are seven 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,buck: Charger buck configuration and status
|
||||||
|
reporting with regards to several regulation
|
||||||
|
loops such as vdd, ibat etc.
|
||||||
|
- 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 buck frequency
|
||||||
|
settings, comparator override features etc.
|
||||||
|
|
||||||
|
Parent node required properties:
|
||||||
|
- qcom,vddmax-mv: Target voltage of battery in mV.
|
||||||
|
- qcom,vddsafe-mv: Maximum Vdd voltage in mV.
|
||||||
|
- qcom,vinmin-mv: Minimum input voltage in mV.
|
||||||
|
- qcom,ibatmax-ma: Maximum battery charge current in mA
|
||||||
|
- qcom,ibatsafe-ma: Safety battery current setting
|
||||||
|
- qcom,thermal-mitigation: Array of ibatmax values for different
|
||||||
|
system thermal mitigation level.
|
||||||
|
|
||||||
|
Parent node optional properties:
|
||||||
|
- qcom,ibatterm-ma: Current at which charging is terminated when
|
||||||
|
the analog end of charge option is selected.
|
||||||
|
- qcom,maxinput-usb-ma: Maximum input current USB.
|
||||||
|
- qcom,maxinput-dc-ma: Maximum input current DC.
|
||||||
|
- qcom,vbatdet-delta-mv: Battery charging resume delta.
|
||||||
|
- qcom,vbatweak-mv: Weak battery voltage threshold in mV, above which
|
||||||
|
fast charging can start. The supported voltage range is
|
||||||
|
from 2100mV to 3600mV with a step size of 100mV.
|
||||||
|
- qcom,charging-disabled: Set this property to disable charging
|
||||||
|
by default. This can then be overriden
|
||||||
|
writing the the module parameter
|
||||||
|
"charging_disabled".
|
||||||
|
- qcom,duty-cycle-100p: Set this property to enable the 100% duty
|
||||||
|
cycle feature.
|
||||||
|
- qcom,use-default-batt-values: Set this flag to force reporting of
|
||||||
|
battery temperature of 250 decidegree
|
||||||
|
Celsius, state of charge to be 50%
|
||||||
|
and disable charging.
|
||||||
|
- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
|
||||||
|
- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
|
||||||
|
Note that if both warm and cool battery
|
||||||
|
temperatures are set, the corresponding
|
||||||
|
ibatmax and bat-mv properties are
|
||||||
|
required to be set.
|
||||||
|
- qcom,ibatmax-cool-ma: Maximum cool battery charge current.
|
||||||
|
- qcom,ibatmax-warm-ma: Maximum warm battery charge current.
|
||||||
|
- qcom,warm-bat-mv: Warm temperature battery target voltage.
|
||||||
|
- qcom,cool-bat-mv: Cool temperature battery target voltage.
|
||||||
|
- qcom,tchg-mins: Maximum total software initialized charge time.
|
||||||
|
- qcom,bpd-detection: Select a battery presence detection scheme by
|
||||||
|
specifying either "bpd_thm", "bpd_id" or
|
||||||
|
"bpd_thm_id". "bpd_thm" selects the temperature
|
||||||
|
pin, "bpd_id" uses the id pin for battery presence
|
||||||
|
detection, "bpd_thm_id" selects both.
|
||||||
|
If the property is not set, the temperatue pin will
|
||||||
|
be used.
|
||||||
|
- qcom,btc-disabled: If flag is set battery hot and cold monitoring is
|
||||||
|
disabled in hardware. This monitoring is turned on
|
||||||
|
by default.
|
||||||
|
- qcom,batt-hot-percent: Specify a supported hot threshold percentage.
|
||||||
|
Supported thresholds: 25% and 35%. If none is specified
|
||||||
|
hardware defaults will be used.
|
||||||
|
- qcom,batt-cold-percent: Specify a supported cold threshold percentage.
|
||||||
|
Supported thresholds: 70% and 80%. If none is specified
|
||||||
|
hardware defaults will be used.
|
||||||
|
- otg-parent-supply Specify a phandle to a parent supply regulator
|
||||||
|
for the OTG regulator.
|
||||||
|
- boost-parent-supply Specify a phandle to a parent supply regulator
|
||||||
|
for the boost regulator.
|
||||||
|
- qcom,resume-soc Capacity in percent at which charging should resume
|
||||||
|
when a fully charged battery drops below this level.
|
||||||
|
- qcom,chg-vadc Corresponding VADC device's phandle.
|
||||||
|
- qcom,pmic-revid The phandle to the revid node of the pmic on which charger
|
||||||
|
peripheral is present. This property is a must on PMIC chips
|
||||||
|
that exhibit inaccuracies in battery current readings. This
|
||||||
|
phandle is used to check the version of the PMIC and apply
|
||||||
|
necessary software workarounds.
|
||||||
|
- qcom,ext-ovp-present Indicates if an external OVP exists which reduces the
|
||||||
|
overall input resistance of the charge path.
|
||||||
|
- qcom,ovp-monitor-en The ovp is enabled on hw by default. If this flag is
|
||||||
|
set, the charger ovp status is monitored in software.
|
||||||
|
- qcom,ibat-calibration-enabled Indicates if ibat calibration is enabled. This is
|
||||||
|
required for devices which have a ibat trim error
|
||||||
|
causing ibatmax to go out of spec.
|
||||||
|
- qcom,power-stage-reduced Indicates if power stage workaround is enabled. This work
|
||||||
|
around reduces the power stage segments while charging
|
||||||
|
under high load during low battery voltages. It's for
|
||||||
|
improving IADC accuracy while board has a bad layout.
|
||||||
|
- qcom,use-external-rsense A boolean that controls whether BMS will use
|
||||||
|
an external sensor resistor instead of the default
|
||||||
|
RDS of the batfet.
|
||||||
|
- qcom,vbatdet-maxerr-mv This property in mV is a hystersis value for the charge
|
||||||
|
resume voltage property qcom,vbatdet-delta-mv. If this
|
||||||
|
property is not defined it defaults to 50 mV.
|
||||||
|
- qcom,parallel-ovp-mode When this option is enabled, it allows charging through both
|
||||||
|
DC and USB OVP FETs. Please note that this should only
|
||||||
|
be enabled in board designs with PM8941 which have DC_IN
|
||||||
|
and USB_IN connected via a short.
|
||||||
|
- qcom,ext-ovp-isns-enable-gpio External OVP enable GPIO.
|
||||||
|
- qcom,ext-ovp-isns-r-ohm External ISNS OVP resistance in ohm.
|
||||||
|
|
||||||
|
Sub node required structure:
|
||||||
|
- A qcom,chg node must be a child of an spmi device. Each subnode reflects
|
||||||
|
a hardware peripheral which adds a unique set of features
|
||||||
|
to the collective charging device. For example USB detection
|
||||||
|
and the battery interface are each seperate peripherals and
|
||||||
|
each should be their own subnode.
|
||||||
|
- qcom,chg-adc_tm Corresponding ADC TM device's phandle to set recurring
|
||||||
|
measurements and receive notification for batt_therm.
|
||||||
|
|
||||||
|
Sub node required properties:
|
||||||
|
- compatible: Must be "qcom,qpnp-charger".
|
||||||
|
- reg: Specifies the SPMI address and size 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,usb-chgpth:
|
||||||
|
- usbin-valid
|
||||||
|
- usb-ocp (only for SMBBP and SMBCL)
|
||||||
|
|
||||||
|
qcom,chgr:
|
||||||
|
- chg-done
|
||||||
|
- chg-failed
|
||||||
|
|
||||||
|
The following interrupts are available:
|
||||||
|
|
||||||
|
qcom,chgr:
|
||||||
|
- chg-done: Triggers on charge completion.
|
||||||
|
- chg-failed: Notifies of charge failures.
|
||||||
|
- fast-chg-on: Notifies of fast charging state.
|
||||||
|
- trkl-chg-on: Indicates trickle charging.
|
||||||
|
- state-change: Notifies of a state change in
|
||||||
|
the charger state machine.
|
||||||
|
- chgwdog: Charger watchdog interrupt.
|
||||||
|
- vbat-det-hi: Triggers on vbat-det-hi voltage
|
||||||
|
setting,can be used as
|
||||||
|
battery alarm.
|
||||||
|
- vbat-det-hi: Triggers on vbat-det-low voltage
|
||||||
|
setting, can be used as
|
||||||
|
battery alarm.
|
||||||
|
|
||||||
|
qcom,buck:
|
||||||
|
- vdd-loop: VDD loop change interrupt.
|
||||||
|
- ibat-loop: Ibat loop change interrupt.
|
||||||
|
- ichg-loop: Charge current loop change.
|
||||||
|
- vchg-loop: Charge voltage loop change.
|
||||||
|
- overtemp: Overtemperature interrupt.
|
||||||
|
- vref-ov: Reference overvoltage interrupt.
|
||||||
|
- vbat-ov: Battery overvoltage interrupt.
|
||||||
|
|
||||||
|
qcom,bat-if:
|
||||||
|
- psi: PMIC serial interface interrupt.
|
||||||
|
- vcp-on: Voltage collapse protection
|
||||||
|
status interrupt.
|
||||||
|
- bat-fet-on: BATFET status interrupt.
|
||||||
|
- bat-temp-ok: Battery temperature status
|
||||||
|
interrupt.
|
||||||
|
- batt-pres: Battery presence status
|
||||||
|
interrupt.
|
||||||
|
|
||||||
|
qcom,usb-chgpth:
|
||||||
|
- usbin-valid: Indicates valid USB connection.
|
||||||
|
- coarse-det-usb: Coarse detect interrupt triggers
|
||||||
|
at low voltage on USB_IN.
|
||||||
|
- chg-gone: Triggers on VCHG line.
|
||||||
|
- usb-ocp Triggers on over current conditions when
|
||||||
|
reverse boosting. (Only available on
|
||||||
|
SMBCL and SMBBP devices).
|
||||||
|
|
||||||
|
qcom,dc-chgpth:
|
||||||
|
- dcin-valid: Indicates a valid DC charger
|
||||||
|
connection.
|
||||||
|
- coarse-det-dc: Coarse detect interrupt triggers
|
||||||
|
at low voltage on DC_IN.
|
||||||
|
|
||||||
|
qcom,boost:
|
||||||
|
- limit-error: Limiting error on SMBB boost.
|
||||||
|
- boost-pwr-ok: Status of boost power.
|
||||||
|
|
||||||
|
Sub node optional properties:
|
||||||
|
qcom,usb-chgpth:
|
||||||
|
- regulator-name: A string used as a descriptive name
|
||||||
|
for the OTG regulator.
|
||||||
|
qcom,boost:
|
||||||
|
- regulator-min-microvolt: Minimum boost voltage setting.
|
||||||
|
- regulator-max-microvolt: Maximum boost voltage setting.
|
||||||
|
- regulator-name: A string used as a descriptive name
|
||||||
|
for the boost regulator.
|
||||||
|
|
||||||
|
qcom,batfet:
|
||||||
|
- regulator-name: A string used as a descriptive name
|
||||||
|
for the batfet regulator.
|
||||||
|
|
||||||
|
qcom,chgr:
|
||||||
|
- regulator-name: A string used as a descriptive name
|
||||||
|
for the flash workarounds regulator.
|
||||||
|
Example:
|
||||||
|
pm8941-chg {
|
||||||
|
compatible = "qcom,qpnp-charger";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
otg-parent-supply = <&pm8941_boost>;
|
||||||
|
boost-parent-supply = <&foo_parent_reg>;
|
||||||
|
|
||||||
|
qcom,vddmax-mv = <4200>;
|
||||||
|
qcom,vddsafe-mv = <4200>;
|
||||||
|
qcom,vinmin-mv = <4200>;
|
||||||
|
qcom,ibatmax-ma = <1500>;
|
||||||
|
qcom,ibatterm-ma = <200>;
|
||||||
|
qcom,ibatsafe-ma = <1500>;
|
||||||
|
qcom,vbatweak-mv = <3200>;
|
||||||
|
qcom,thermal-mitigation = <1500 700 600 325>;
|
||||||
|
qcom,cool-bat-degc = <10>;
|
||||||
|
qcom,cool-bat-mv = <4100>;
|
||||||
|
qcom,ibatmax-warm-ma = <350>;
|
||||||
|
qcom,warm-bat-degc = <45>;
|
||||||
|
qcom,warm-bat-mv = <4100>;
|
||||||
|
qcom,ibatmax-cool-ma = <350>;
|
||||||
|
qcom,vbatdet-delta-mv = <60>;
|
||||||
|
qcom,batt-hot-percent = <25>;
|
||||||
|
qcom,batt-cold-percent = <85>;
|
||||||
|
qcom,btc-disabled = <0>;
|
||||||
|
qcom,chg-vadc = <&pm8941_vadc>;
|
||||||
|
qcom,chg-adc_tm = <&pm8941_adc_tm>;
|
||||||
|
qcom,pmic-revid = <&pm8941_revid>;
|
||||||
|
|
||||||
|
qcom,chgr@1000 {
|
||||||
|
reg = <0x1000 0x100>;
|
||||||
|
interrupts = <0x0 0x10 0x0>,
|
||||||
|
<0x0 0x10 0x1>,
|
||||||
|
<0x0 0x10 0x2>,
|
||||||
|
<0x0 0x10 0x3>,
|
||||||
|
<0x0 0x10 0x4>,
|
||||||
|
<0x0 0x10 0x5>,
|
||||||
|
<0x0 0x10 0x6>,
|
||||||
|
<0x0 0x10 0x7>;
|
||||||
|
|
||||||
|
interrupt-names = "chg-done",
|
||||||
|
"chg-failed",
|
||||||
|
"fast-chg-on",
|
||||||
|
"trkl-chg-on",
|
||||||
|
"state-change",
|
||||||
|
"chgwdog",
|
||||||
|
"vbat-det-hi",
|
||||||
|
"vbat-det-lo";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,buck@1100 {
|
||||||
|
reg = <0x1100 0x100>;
|
||||||
|
interrupts = <0x0 0x11 0x0>,
|
||||||
|
<0x0 0x11 0x1>,
|
||||||
|
<0x0 0x11 0x2>,
|
||||||
|
<0x0 0x11 0x3>,
|
||||||
|
<0x0 0x11 0x4>,
|
||||||
|
<0x0 0x11 0x5>,
|
||||||
|
<0x0 0x11 0x6>;
|
||||||
|
|
||||||
|
interrupt-names = "vdd-loop",
|
||||||
|
"ibat-loop",
|
||||||
|
"ichg-loop",
|
||||||
|
"vchg-loop",
|
||||||
|
"overtemp",
|
||||||
|
"vref-ov",
|
||||||
|
"vbat-ov";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,bat-if@1200 {
|
||||||
|
reg = <0x1200 0x100>;
|
||||||
|
interrupts = <0x0 0x12 0x0>,
|
||||||
|
<0x0 0x12 0x1>,
|
||||||
|
<0x0 0x12 0x2>,
|
||||||
|
<0x0 0x12 0x3>,
|
||||||
|
<0x0 0x12 0x4>;
|
||||||
|
|
||||||
|
interrupt-names = "psi",
|
||||||
|
"vcp-on",
|
||||||
|
"bat-fet-on",
|
||||||
|
"bat-temp-ok",
|
||||||
|
"batt-pres";
|
||||||
|
};
|
||||||
|
|
||||||
|
pm8941_chg_otg: qcom,usb-chgpth@1300 {
|
||||||
|
reg = <0x1300 0x100>;
|
||||||
|
interrupts = <0 0x13 0x0>,
|
||||||
|
<0 0x13 0x1>,
|
||||||
|
<0x0 0x13 0x2>;
|
||||||
|
|
||||||
|
interrupt-names = "usbin-valid",
|
||||||
|
"coarse-det-usb",
|
||||||
|
"chg-gone";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,dc-chgpth@1400 {
|
||||||
|
reg = <0x1400 0x100>;
|
||||||
|
interrupts = <0x0 0x14 0x0>,
|
||||||
|
<0x0 0x14 0x1>;
|
||||||
|
|
||||||
|
interrupt-names = "dcin-valid",
|
||||||
|
"coarse-det-dc";
|
||||||
|
};
|
||||||
|
|
||||||
|
pm8941_chg_boost: qcom,boost@1500 {
|
||||||
|
reg = <0x1500 0x100>;
|
||||||
|
interrupts = <0x0 0x15 0x0>,
|
||||||
|
<0x0 0x15 0x1>;
|
||||||
|
|
||||||
|
interrupt-names = "limit-error",
|
||||||
|
"boost-pwr-ok";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,misc@1600 {
|
||||||
|
reg = <0x1600 0x100>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
In regulator specific device tree file:
|
||||||
|
|
||||||
|
&pm8941_chg_boost {
|
||||||
|
regulator-min-microvolt = <5000000>;
|
||||||
|
regulator-max-microvolt = <5000000>;
|
||||||
|
regulator-name = "8941_smbb_boost";
|
||||||
|
};
|
||||||
|
|
||||||
|
&pm8941_chg_batif {
|
||||||
|
regulator-name = "batfet";
|
||||||
|
};
|
||||||
|
|
||||||
|
&pm8941_chg_otg {
|
||||||
|
regulator-name = "8941_smbb_otg";
|
||||||
|
};
|
265
Documentation/devicetree/bindings/power/qpnp-fg.txt
Normal file
265
Documentation/devicetree/bindings/power/qpnp-fg.txt
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
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,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>;
|
||||||
|
};
|
||||||
|
};
|
209
Documentation/devicetree/bindings/power/qpnp-linear-charger.txt
Normal file
209
Documentation/devicetree/bindings/power/qpnp-linear-charger.txt
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
Qualcomm QPNP Linear Charger
|
||||||
|
|
||||||
|
The charger module supports the linear battery charger peripherals on
|
||||||
|
Qualcomm PMIC chips.
|
||||||
|
|
||||||
|
There are four different peripherals in the charger module.
|
||||||
|
Each of these peripherals are implemented as subnodes.
|
||||||
|
|
||||||
|
- qcom,chgr: Supports charging control and status reporting
|
||||||
|
- qcom,bat-if: Battery status reporting such as presence and
|
||||||
|
temperature reporting.
|
||||||
|
- qcom,usb-chgpth: USB charge path detection and input current
|
||||||
|
limiting configuration.
|
||||||
|
- qcom,chg-misc: Miscellaneous features such as comparator override
|
||||||
|
features etc.
|
||||||
|
|
||||||
|
Parent node required properties:
|
||||||
|
- qcom,vddmax-mv: Target voltage of battery in mV.
|
||||||
|
- qcom,vddsafe-mv: Maximum Vdd voltage in mV.
|
||||||
|
- qcom,vinmin-mv: Minimum input voltage in mV.
|
||||||
|
- qcom,ibatsafe-ma: Safety battery current setting
|
||||||
|
|
||||||
|
Parent node optional properties:
|
||||||
|
- qcom,vbatweak-uv: Weak battery voltage threshold in uV,
|
||||||
|
above which fast charging can start.
|
||||||
|
The supported voltage range is from
|
||||||
|
3000000uV to 3581250uV with a step
|
||||||
|
size of 18750000 uV.
|
||||||
|
- qcom,charging-disabled: Set this property to disable charging
|
||||||
|
by default.
|
||||||
|
- qcom,use-default-batt-values: Set this flag to force reporting of
|
||||||
|
fake battery.
|
||||||
|
- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
|
||||||
|
- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
|
||||||
|
Note that if both warm and cool
|
||||||
|
battery temperatures are set, the
|
||||||
|
corresponding ibatmax and bat-mv
|
||||||
|
properties are required to be set.
|
||||||
|
- qcom,ibatmax-cool-ma: Maximum cool battery charge current.
|
||||||
|
- qcom,ibatmax-warm-ma: Maximum warm battery charge current.
|
||||||
|
- qcom,warm-bat-mv: Warm temperature battery target
|
||||||
|
voltage.
|
||||||
|
- qcom,cool-bat-mv: Cool temperature battery target
|
||||||
|
voltage.
|
||||||
|
- qcom,thermal-mitigation: Array of ibatmax values for different
|
||||||
|
system thermal mitigation level.
|
||||||
|
- qcom,tchg-mins: Maximum total software initialized
|
||||||
|
charge time.
|
||||||
|
- qcom,bpd-detection: Select a battery presence detection
|
||||||
|
scheme by specifying either "bpd_thm"
|
||||||
|
"bpd_id" or "bpd_thm_id". "bpd_thm"
|
||||||
|
selects the temperature pin, "bpd_id"
|
||||||
|
uses the id pin for battery presence
|
||||||
|
detection, "bpd_thm_id" selects both.
|
||||||
|
If the property is not set, the
|
||||||
|
temperatue pin will be used.
|
||||||
|
- qcom,btc-disabled: If flag is set battery hot and cold
|
||||||
|
monitoring is disabled in hardware.
|
||||||
|
This monitoring is turned on by
|
||||||
|
default.
|
||||||
|
- qcom,batt-hot-percentage: Specify a supported hot threshold
|
||||||
|
percentage.
|
||||||
|
Supported thresholds: 25% and 35%. If
|
||||||
|
none is specified hardware defaults
|
||||||
|
will be used.
|
||||||
|
- qcom,batt-cold-percentage: Specify a supported cold threshold
|
||||||
|
percentage. Supported thresholds: 70%
|
||||||
|
and 80%. If none is specified
|
||||||
|
hardwaredefaults will be used.
|
||||||
|
- qcom,chg-adc_tm Corresponding ADC TM device's phandle
|
||||||
|
to set recurring measurements and
|
||||||
|
receive notification for batt_therm.
|
||||||
|
-qcom,float-charge If specified enable float charging.
|
||||||
|
- qcom,chg-vadc Corresponding VADC device's phandle.
|
||||||
|
- qcom,charger-detect-eoc If specified charger hardware will
|
||||||
|
detect end-of-charge.
|
||||||
|
If not specified charger driver
|
||||||
|
depends on BMSfor end-of-charge
|
||||||
|
detection.
|
||||||
|
- qcom,disable-vbatdet-based-recharge If specified disable VBATDET irq
|
||||||
|
and charging can only be resumed
|
||||||
|
if charger is re-inserted or SOC
|
||||||
|
falls below resume SOC.
|
||||||
|
This property should always be used
|
||||||
|
along with the BMS property:
|
||||||
|
"qcom,disable-suspend-on-usb".
|
||||||
|
- qcom,use-external-charger If specifed the LBC module will
|
||||||
|
be disabled and the driver will not
|
||||||
|
register. It also enables BID for
|
||||||
|
BPD and disables BTC. Declare this node
|
||||||
|
only if you are using an external charger
|
||||||
|
and not the PMIC internal LBC.
|
||||||
|
- qcom,chgr-led-support There is a current sink device in linear
|
||||||
|
charger module, it is used to control a
|
||||||
|
led which can act as a charger led as well
|
||||||
|
as a general notification led.
|
||||||
|
- qcom,parallel-charger This is a bool property to indicate the
|
||||||
|
LBC will operate as a secondary charger
|
||||||
|
in the parallel mode. If this is enabled
|
||||||
|
the charging operations will be controlled by
|
||||||
|
the primary-charger.
|
||||||
|
- qcom,collapsible-chgr-support If specifed the collapsible charger feature
|
||||||
|
will be supported. LBC will disable VIN_MIN
|
||||||
|
comparator and use chg_gone interrupt to
|
||||||
|
detect charger removal.
|
||||||
|
|
||||||
|
|
||||||
|
Sub node required structure:
|
||||||
|
- A qcom,charger node must be a child of an spmi_device nod. Each subnode
|
||||||
|
reflects a hardware peripheral which adds a unique set of features
|
||||||
|
to the collective charging device. For example USB detection
|
||||||
|
and the battery interface are each seperate peripherals and
|
||||||
|
each should be their own subnode.
|
||||||
|
|
||||||
|
Sub node required properties:
|
||||||
|
- compatible: Must be "qcom,qpnp-linear-charger".
|
||||||
|
- reg: Specifies the SPMI address and size 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,usb-chgpth:
|
||||||
|
- usbin-valid
|
||||||
|
|
||||||
|
The following interrupts are available:
|
||||||
|
|
||||||
|
qcom,usb-chgpth:
|
||||||
|
- usbin-valid: Indicates valid USB
|
||||||
|
connection.
|
||||||
|
- coarse-det-usb: Coarse detect interrupt
|
||||||
|
triggers at low voltage on
|
||||||
|
USB_IN.
|
||||||
|
- chg-gone: Triggers on VCHG line.
|
||||||
|
- overtemp: Triggers on over temperature
|
||||||
|
condition
|
||||||
|
|
||||||
|
qcom,chgr:
|
||||||
|
- chg-done: Triggers on charge completion.
|
||||||
|
- chg-failed: Notifies of charge failures.
|
||||||
|
- fast-chg-on: Notifies of fast charging.
|
||||||
|
- vbat-det-lo: Triggers on vbat-det-lo
|
||||||
|
voltage.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
pm8916-chg: qcom,charger {
|
||||||
|
compatible = "qcom,qpnp-linear-charger";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
qcom,vddmax-mv = <4200>;
|
||||||
|
qcom,vddsafe-mv = <4200>;
|
||||||
|
qcom,vinmin-mv = <4200>;
|
||||||
|
qcom,ibatsafe-ma = <1440>;
|
||||||
|
qcom,vbatweak-uv = <3200>;
|
||||||
|
qcom,thermal-mitigation = <1500 700 600 325>;
|
||||||
|
qcom,cool-bat-decidegc = <100>;
|
||||||
|
qcom,warm-bat-decidegc = <450>;
|
||||||
|
qcom,cool-bat-mv = <4100>;
|
||||||
|
qcom,ibatmax-warm-ma = <360>;
|
||||||
|
qcom,ibatmax-cool-ma = <360>;
|
||||||
|
qcom,warm-bat-mv = <4100>;
|
||||||
|
qcom,batt-hot-percentage = <25>;
|
||||||
|
qcom,batt-cold-percentage = <85>;
|
||||||
|
qcom,tchg-mins = <152>;
|
||||||
|
qcom,resume-soc = <99>;
|
||||||
|
qcom,btc-disabled = <0>;
|
||||||
|
qcom,chg-vadc = <&pm8916_vadc>;
|
||||||
|
|
||||||
|
qcom,chgr@1000 {
|
||||||
|
reg = <0x1000 0x100>;
|
||||||
|
interrupts = <0x0 0x10 0x7>,
|
||||||
|
<0x0 0x10 0x6>,
|
||||||
|
<0x0 0x10 0x5>,
|
||||||
|
<0x0 0x10 0x0>;
|
||||||
|
|
||||||
|
interrupt-names = "chg-done",
|
||||||
|
"chg-failed",
|
||||||
|
"fast-chg-on",
|
||||||
|
"vbat-det-lo";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,bat-if@1200 {
|
||||||
|
reg = <0x1200 0x100>;
|
||||||
|
interrupts = <0x0 0x12 0x1>,
|
||||||
|
<0x0 0x12 0x0>;
|
||||||
|
|
||||||
|
interrupt-names = "bat-temp-ok",
|
||||||
|
"batt-pres";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,usb-chgpth@1300 {
|
||||||
|
reg = <0x1300 0x100>;
|
||||||
|
interrupts = <0 0x13 0x2>,
|
||||||
|
<0 0x13 0x1>;
|
||||||
|
|
||||||
|
interrupt-names = "chg-gone",
|
||||||
|
"usbin-valid";
|
||||||
|
};
|
||||||
|
|
||||||
|
qcom,chg-misc@1600 {
|
||||||
|
reg = <0x1600 0x100>;
|
||||||
|
};
|
||||||
|
};
|
394
Documentation/devicetree/bindings/power/qpnp-smbcharger.txt
Normal file
394
Documentation/devicetree/bindings/power/qpnp-smbcharger.txt
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
179
Documentation/devicetree/bindings/power/qpnp-vm-bms.txt
Normal file
179
Documentation/devicetree/bindings/power/qpnp-vm-bms.txt
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
Qualcomm's QPNP Voltage-Mode(VM) PMIC Battery Management System
|
||||||
|
|
||||||
|
QPNP PMIC VM BMS provides interface to clients to read properties related
|
||||||
|
to the battery. Its main function is to calculate the SOC (state of charge)
|
||||||
|
of the battery based on periodic sampling of the VBAT (battery voltage).
|
||||||
|
|
||||||
|
Parent node required properties:
|
||||||
|
- compatible : Must be "qcom,qpnp-vm-bms" for the BM driver.
|
||||||
|
- 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 bms interrupt
|
||||||
|
The following interrupts are required:
|
||||||
|
0 : leave CV state
|
||||||
|
1 : enter CV state
|
||||||
|
2 : good ocv generated
|
||||||
|
3 : ocv_thr
|
||||||
|
4 : fifo update
|
||||||
|
5 : fsm state chnaged
|
||||||
|
|
||||||
|
Additionally, optional subnodes may be included:
|
||||||
|
- qcom,batt-pres-status : A subnode with a register address for the SMBB
|
||||||
|
battery interface's BATT_PRES_STATUS register. If this node is
|
||||||
|
added, then the BMS will try to detect offmode battery removal
|
||||||
|
via the battery interface's offmode battery removal circuit.
|
||||||
|
- qcom,battery-data : A phandle to a node containing the available batterydata
|
||||||
|
profiles. See the batterydata bindings documentation for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
Parent node required properties:
|
||||||
|
- qcom,v-cutoff-uv : cutoff voltage where the battery is considered dead in
|
||||||
|
micro-volts.
|
||||||
|
- qcom,max-voltage-uv : maximum voltage for the battery in micro-volts.
|
||||||
|
- qcom,r-conn-mohm : connector resistance in milli-ohms.
|
||||||
|
- qcom,shutdown-soc-valid-limit : If the ocv upon restart is within this
|
||||||
|
distance of the shutdown ocv, the BMS will try to force
|
||||||
|
the new SoC to the old one to provide charge continuity.
|
||||||
|
That is to say,
|
||||||
|
if (abs(shutdown-soc - current-soc) < limit)
|
||||||
|
then use old SoC.
|
||||||
|
- qcom,low-soc-calculate-soc-threshold : The SoC threshold for when
|
||||||
|
the periodic calculate_soc work speeds up. This ensures
|
||||||
|
SoC is updated in userspace constantly when we are near
|
||||||
|
shutdown.
|
||||||
|
- qcom,low-voltage-threshold : The battery voltage threshold in micro-volts for
|
||||||
|
when the BMS tries to wake up and hold a wakelock to
|
||||||
|
ensure a clean shutdown.
|
||||||
|
- qcom,low-voltage-calculate-soc-ms : The time period between subsequent
|
||||||
|
SoC recalculations when the current voltage is below
|
||||||
|
qcom,low-voltage threshold. This takes precedence over
|
||||||
|
qcom,low-soc-calculate-soc-ms.
|
||||||
|
- qcom,low-soc-calculate-soc-ms : The time period between subsequent
|
||||||
|
SoC recalculations when the current SoC is below
|
||||||
|
qcom,low-soc-calculate-soc-threshold. This takes
|
||||||
|
precedence over qcom,calculate-soc-ms.
|
||||||
|
- qcom,calculate-soc-ms : The time period between subsequent SoC
|
||||||
|
recalculations when the current SoC is above or equal
|
||||||
|
qcom,low-soc-calculate-soc-threshold.
|
||||||
|
- qcom,volatge-soc-timeout-ms : The timeout period after which the module starts
|
||||||
|
reporting volage based SOC and does not use the VMBMS
|
||||||
|
algorithm for SOC calculation.
|
||||||
|
- qcom,bms-vadc: Corresponding VADC device's phandle.
|
||||||
|
- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring
|
||||||
|
measurements and receive notifications for vbatt.
|
||||||
|
- qcom,pmic-revid : Phandle pointing to the revision peripheral node.
|
||||||
|
|
||||||
|
Parent node Optional properties
|
||||||
|
- qcom,s1-sample-interval-ms: The sampling rate in ms of the accumulator in state
|
||||||
|
S1. (i.e) the rate at which the accumulator is being
|
||||||
|
filled with vbat samples. Minimum value = 0 and
|
||||||
|
Maximum value = 2550ms.
|
||||||
|
- qcom,s2-sample-interval-ms: The sampling rate in ms of the accumulator in state
|
||||||
|
S2. (i.e) the rate at which the accumulator is being
|
||||||
|
filled with vbat samples. Minimum value = 0 and
|
||||||
|
Maximum value = 2550ms.
|
||||||
|
- qcom,s1-sample-count: The number of samples to be accululated for one FIFO in
|
||||||
|
state S1. Possible values are - 0, 4, 8, 16, 32, 64, 128,
|
||||||
|
256.
|
||||||
|
- qcom,s2-sample-count: The number of samples to be accululated for one FIFO in
|
||||||
|
state S2. Possible values are - 0, 4, 8, 16, 32, 64, 128,
|
||||||
|
256.
|
||||||
|
- qcom,s1-fifo-legth: Number of FIFO's to be filled in state S1, to generate
|
||||||
|
the fifo_update_done interrupt. Possile values - 0 to 8
|
||||||
|
- qcom,s2-fifo-legth: Number of FIFO's to be filled in state S2, to generate
|
||||||
|
the fifo_update_done interrupt. Possible values- 0 to 8
|
||||||
|
- qcom,force-s3-on-suspend : Bool property to force the BMS into S3 (sleep) state
|
||||||
|
while entering into system suspend.
|
||||||
|
- qcom,force-bms-active-on-charger: Bool property to keep BMS FSM active
|
||||||
|
if charger is present.
|
||||||
|
- qcom,report-charger-eoc : Bool property to indicate if BMS needs to indicate
|
||||||
|
EOC to charger.
|
||||||
|
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
|
||||||
|
try to force the startup SoC to be the same as the
|
||||||
|
shutdown SoC. Defining it will make BMS ignore the
|
||||||
|
shutdown SoC.
|
||||||
|
- qcom,use-voltage-soc : A boolean that controls whether BMS will use
|
||||||
|
voltage-based SoC instead of a coulomb counter based
|
||||||
|
one. Voltage-based SoC will not guarantee linearity.
|
||||||
|
- qcom,disable-bms : Bool property to disable the VMBMS hardware module.
|
||||||
|
Enable this property if BMS is not supported or an external
|
||||||
|
fuel gauge is used.
|
||||||
|
- qcom,s3-ocv-tolerence-uv : The S3 state OCV tolerence threshold in uV. The
|
||||||
|
LSB value is 300uV and maximum value is 76500uV.
|
||||||
|
- qcom,low-soc-fifo-length : The fifo length (of S2 STATE) to be used at lower
|
||||||
|
SOCs. If this value is not specified the system uses
|
||||||
|
default lenght.
|
||||||
|
- qcom,resume-soc: Capacity in percent at which charging should resume
|
||||||
|
when a fully charged battery drops below this level.
|
||||||
|
- qcom,low-temp-threshold : The temperature threshold below which the IBAT
|
||||||
|
averaging and UUC smoothening is disabled. This value
|
||||||
|
is in deci-degrees centigrade. If not specified it
|
||||||
|
defaults to 0.
|
||||||
|
- qcom,ibat-avg-samples : The number of samples to be averaged for IBAT
|
||||||
|
estimation. If not specified it defaults to 16.
|
||||||
|
The possible values are 1 to 16.
|
||||||
|
- qcom,batt-aging-comp : A boolean that defines if battery aging compensation
|
||||||
|
is enabled.
|
||||||
|
- qcom,use-reported-soc : Bool property to enable the reported_soc logic. To
|
||||||
|
enable this feature, qcom,resume-soc must be defined as
|
||||||
|
a proper value. The BMS is also required to control the
|
||||||
|
charging, discharging and recharging.
|
||||||
|
|
||||||
|
qcom,batt-pres-status node required properties:
|
||||||
|
- reg : offset and length of the PMIC LBC battery interface BATT_PRES_STATUS
|
||||||
|
register.
|
||||||
|
|
||||||
|
qcom,qpnp-chg-pres required properties:
|
||||||
|
- reg : offset and length of the PMIC LBC charger interafce CHARGER_OPTION
|
||||||
|
register.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
pm8916_bms: qcom,qpnp-vm-bms {
|
||||||
|
compatible = "qcom,qpnp-vm-bms";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
status = "disabled";
|
||||||
|
|
||||||
|
qcom,v-cutoff-uv = <3400000>;
|
||||||
|
qcom,max-voltage-uv = <4200000>;
|
||||||
|
qcom,r-conn-mohm = <18>;
|
||||||
|
qcom,shutdown-soc-valid-limit = <20>;
|
||||||
|
qcom,low-soc-calculate-soc-threshold = <15>;
|
||||||
|
qcom,low-voltage-threshold = <3420000>;
|
||||||
|
qcom,low-voltage-calculate-soc-ms = <1000>;
|
||||||
|
qcom,low-soc-calculate-soc-ms = <5000>;
|
||||||
|
qcom,low-soc-fifo-length = <2>;
|
||||||
|
qcom,calculate-soc-ms = <20000>;
|
||||||
|
qcom,s3-ocv-tolerence-uv = <1200>;
|
||||||
|
qcom,volatge-soc-timeout-ms = <60000>;
|
||||||
|
qcom,battery-data = <&mtp_batterydata>;
|
||||||
|
qcom,bms-vadc = <&pm8916_vadc>;
|
||||||
|
qcom,bms-adc_tm = <&pm8916_adc_tm>;
|
||||||
|
|
||||||
|
qcom,batt-pres-status@1208 {
|
||||||
|
reg = <0x1208 0x1>;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcom,qpnp-chg-pres@1208 {
|
||||||
|
reg = <0x1108 0x1>;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcom,bms-bms@4000 {
|
||||||
|
reg = <0x4000 0x100>;
|
||||||
|
interrupts = <0x0 0x40 0x0>,
|
||||||
|
<0x0 0x40 0x1>,
|
||||||
|
<0x0 0x40 0x2>,
|
||||||
|
<0x0 0x40 0x3>,
|
||||||
|
<0x0 0x40 0x4>,
|
||||||
|
<0x0 0x40 0x5>,
|
||||||
|
|
||||||
|
interrupt-names = "leave_cv",
|
||||||
|
"enter_cv",
|
||||||
|
"good_ocv",
|
||||||
|
"ocv_thr",
|
||||||
|
"fifo_updtaed",
|
||||||
|
"fsm_state_change";
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,265 @@
|
||||||
|
QTI's LAB (LCD/AMOLED BOOST)/IBB (Inverting Buck-Boost) Regulator
|
||||||
|
|
||||||
|
LAB can be used as a standalone positive boost power supply for general purpose
|
||||||
|
applications. IBB can be used as a standalone negative power supply for general
|
||||||
|
applications. Also, LAB and IBB can be used together to provide power supply for
|
||||||
|
display panels, LCD or AMOLED.
|
||||||
|
|
||||||
|
Main node required properties:
|
||||||
|
|
||||||
|
- compatible: Must be "qcom,qpnp-labibb-regulator"
|
||||||
|
- qpnp,qpnp-labibb-mode: A string used to specify the working mode of LAB/IBB
|
||||||
|
regulators when bootloader does not turned on the
|
||||||
|
display panel. Could be "lcd", "amoled" or
|
||||||
|
"stand-alone".
|
||||||
|
"lcd" means using LAB and IBB regulators are
|
||||||
|
configured for LCD mode together.
|
||||||
|
"amoled" means using LAB and IBB regulators are
|
||||||
|
configured for AMOLED mode together.
|
||||||
|
"stand-alone" means using LAB and IBB regulators
|
||||||
|
as stand alone regulators.
|
||||||
|
|
||||||
|
Main node optional properties:
|
||||||
|
|
||||||
|
- qcom,qpnp-labibb-touch-to-wake-en: A boolean property which upon set will
|
||||||
|
enable support for touch-to-wake mode
|
||||||
|
by configuring the required settings
|
||||||
|
in LAB and IBB modules. Make sure the
|
||||||
|
hardware has needed support before
|
||||||
|
enabling this property.
|
||||||
|
- qpnp,swire-control: A bool property which indicates if the LAB/IBB is
|
||||||
|
controlled by the SWIRE interface. Enable only
|
||||||
|
if qpnp,qpnp-labibb-mode = "amoled".
|
||||||
|
|
||||||
|
LAB subnode required properties:
|
||||||
|
|
||||||
|
- reg: Specifies the SPMI address and size for this peripheral.
|
||||||
|
- reg-names: Register names. Must be "lab".
|
||||||
|
- regulator-name: A string used to describe the regulator.
|
||||||
|
- regulator-min-microvolt: Minimum voltage in microvolts supported by this regulator.
|
||||||
|
- regulator-max-microvolt: Maximum voltage in microvolts supported by this regulator.
|
||||||
|
|
||||||
|
- qcom,qpnp-lab-min-voltage: The minimum voltage in microvolts LAB regulator can support.
|
||||||
|
- qcom,qpnp-lab-step-size: The step size in microvolts of LAB regulator.
|
||||||
|
- qcom,qpnp-lab-slew-rate: The time in us taken by the regulator to change
|
||||||
|
voltage value in one step.
|
||||||
|
|
||||||
|
- qcom,qpnp-lab-init-voltage: The default initial voltage when the bootloader
|
||||||
|
does not turn on LAB regulator.
|
||||||
|
- qcom,qpnp-lab-init-amoled-voltage: The default output voltage when LAB regulator
|
||||||
|
is configured in amoled mode.
|
||||||
|
- qcom,qpnp-lab-init-lcd-voltage: The default output voltage when LAB regulator
|
||||||
|
is configured in lcd mode.
|
||||||
|
- qcom,qpnp-lab-soft-start: The soft start time in us of LAB regulator.
|
||||||
|
Supported value are 200, 400, 600 and 800.
|
||||||
|
|
||||||
|
- qcom,qpnp-lab-ps-threshold: The threshold in mA of Pulse Skip Mode for
|
||||||
|
LAB regulator. Supported values are 20, 30,
|
||||||
|
40 and 50.
|
||||||
|
- qcom,qpnp-lab-pfet-size: PFET size in percentage. Supported values
|
||||||
|
are 25, 50, 75 and 100.
|
||||||
|
- qcom,qpnp-lab-nfet-size: NFET size in percentage. Supported values
|
||||||
|
are 25, 50, 75 and 100.
|
||||||
|
- qcom,qpnp-lab-max-precharge-time: Precharge time in us of LAB regulator.
|
||||||
|
Supported values are 200, 300, 400 and 500.
|
||||||
|
- qcom,qpnp-lab-switching-clock-frequency: The PWM switching clock frequency in
|
||||||
|
kHz of Lab regulator, Supported values
|
||||||
|
are: 3200, 2740, 2400, 2130, 1920,
|
||||||
|
1750, 1600, 1480, 1370, 1280, 1200,
|
||||||
|
1130, 1070, 1010, 960, 910.
|
||||||
|
- qcom,qpnp-lab-limit-maximum-current: The maximum inductor current limit in
|
||||||
|
mA of LAB regulator. Supported values
|
||||||
|
are 200, 400, 600 and 800.
|
||||||
|
|
||||||
|
LAB subnode optional properties:
|
||||||
|
|
||||||
|
- qpnp,qpnp-lab-current-sense: If this property is specified, the LAB current
|
||||||
|
sense gain will be programmed for LAB regulator.
|
||||||
|
Otherwise, LAB current sense gain will be
|
||||||
|
default to "1x". A string is used to specify the
|
||||||
|
LAB current sense gain. Could be "0.5x" or "1x"
|
||||||
|
or "1.5x" or "2x". For e.g. "0.5x" means current
|
||||||
|
sense gain is 0.5.
|
||||||
|
- qcom,qpnp-lab-ps-enable: A boolean proerty which upon set will enable
|
||||||
|
pulse skip mode for LAB regulator. Otherwise,
|
||||||
|
it is disabled.
|
||||||
|
- qcom,qpnp-lab-full-pull-down: A boolean property which upon set will enable
|
||||||
|
the pull down strength of LAB regulator to
|
||||||
|
full. Otherwise, the pull down strength is
|
||||||
|
configured to half.
|
||||||
|
- qcom,qpnp-lab-pull-down-enable: A boolean property which upon set will enable
|
||||||
|
the pull down for LAB regulator. Otherwise,
|
||||||
|
it is disabled.
|
||||||
|
- qcom,qpnp-lab-max-precharge-enable: A boolean property which upon set will
|
||||||
|
enable fast precharge. Otherwise, it is
|
||||||
|
disabled.
|
||||||
|
- qcom,qpnp-lab-ring-suppression-enable: A boolean property which upon set will
|
||||||
|
enable ring suppression for LAB
|
||||||
|
regulator. Otherwise, it is disabled.
|
||||||
|
- qcom,qpnp-lab-limit-max-current-enable: A boolean property which upon set will
|
||||||
|
enforce maximum inductor current constraint
|
||||||
|
for LAB regulator. Otherwise, there is no
|
||||||
|
maximum current constraint.
|
||||||
|
- qcom,qpnp-lab-use-default-voltage: A boolean property which upon set will
|
||||||
|
use the value specified in
|
||||||
|
qcom,qpnp-lab-init-voltage property.
|
||||||
|
This will be used only if the bootloader
|
||||||
|
doesn't configure the output voltage
|
||||||
|
already. If it it not specified, then
|
||||||
|
output voltage can be configured to
|
||||||
|
any value in the allowed limit.
|
||||||
|
|
||||||
|
IBB subnode required properties:
|
||||||
|
|
||||||
|
- reg: Specifies the SPMI address and size for this peripheral.
|
||||||
|
- reg-names: Register names. Must be "ibb".
|
||||||
|
- regulator-name: A string used to describe the regulator.
|
||||||
|
- regulator-min-microvolt: Minimum voltage in microvolts supported by this regulator.
|
||||||
|
- regulator-max-microvolt: Maximum voltage in microvolts supported by this regulator.
|
||||||
|
|
||||||
|
- qcom,qpnp-ibb-min-voltage: The minimum voltage in microvolts IBB regulator can support.
|
||||||
|
- qcom,qpnp-ibb-step-size: The step size in microvolts of IBB regulator.
|
||||||
|
- qcom,qpnp-ibb-slew-rate: The time in us taken by the regulator to change
|
||||||
|
voltage value in one step.
|
||||||
|
- qcom,qpnp-ibb-soft-start: The soft start time in us of IBB regulator.
|
||||||
|
|
||||||
|
- qcom,qpnp-ibb-init-voltage: The default initial voltage when the bootloader does
|
||||||
|
not turn on IBB regulator.
|
||||||
|
- qcom,qpnp-ibb-init-amoled-voltage: The default output voltage when IBB regulator
|
||||||
|
is configured in amoled mode.
|
||||||
|
- qcom,qpnp-ibb-init-lcd-voltage: The default output voltage when IBB regulator
|
||||||
|
is configured in lcd mode.
|
||||||
|
|
||||||
|
- qcom,qpnp-ibb-discharge-resistor: The discharge resistor in Kilo Ohms which
|
||||||
|
controls the soft start time. Supported values
|
||||||
|
are 300, 64, 32 and 16.
|
||||||
|
- qcom,qpnp-ibb-lab-pwrup-delay: Power up delay (in us) for IBB regulator when
|
||||||
|
it is enabled or turned on. Supported values
|
||||||
|
are 1000, 2000, 4000 and 8000.
|
||||||
|
- qcom,qpnp-ibb-lab-pwrdn-delay: Power down delay (in us) for IBB regulator
|
||||||
|
when it is disabled or turned off. Supported
|
||||||
|
values are 1000, 2000, 4000 and 8000.
|
||||||
|
|
||||||
|
- qcom,qpnp-ibb-switching-clock-frequency: The PWM switching clock frequency in
|
||||||
|
kHz of IBB regulator. Supported values
|
||||||
|
are: 3200, 2740, 2400, 2130, 1920,
|
||||||
|
1750, 1600, 1480, 1370, 1280, 1200,
|
||||||
|
1130, 1070, 1010, 960, 910.
|
||||||
|
- qcom,qpnp-ibb-limit-maximum-current: The maximum inductor current limit in
|
||||||
|
mA of IBB regulator. Supported values
|
||||||
|
are: 0, 50, 100, 150, 200, 250, 300,
|
||||||
|
350, 400, 450, 500, 550, 600, 650, 700,
|
||||||
|
750, 800, 850, 900, 950, 1000, 1050,
|
||||||
|
1100, 1150, 1200, 1250, 1300, 1350,
|
||||||
|
1400, 1450, 1500 and 1550.
|
||||||
|
- qcom,qpnp-ibb-debounce-cycle: The debounce cycle of IBB regulator.
|
||||||
|
Supported values are 8, 16, 32 and 64.
|
||||||
|
|
||||||
|
IBB subnode optional properties:
|
||||||
|
|
||||||
|
- qcom,qpnp-ibb-ps-enable: A boolean property which upon set will enable
|
||||||
|
pulse skip mode for IBB regulator. Otherwise,
|
||||||
|
it is disabled.
|
||||||
|
- qcom,qpnp-ibb-full-pull-down: A boolean property which upon set will enable
|
||||||
|
the pull down strength of IBB regulator to
|
||||||
|
full. Otherwise, the pull down strength is
|
||||||
|
configured to half.
|
||||||
|
- qcom,qpnp-ibb-pull-down-enable: A boolean property which upon set will enable
|
||||||
|
the pull down for IBB regulator. Otherwise,
|
||||||
|
it is disabled.
|
||||||
|
- qcom,qpnp-ibb-en-discharge: A boolean property which upon set will
|
||||||
|
enable discharge for IBB regulator.
|
||||||
|
Otherwise, it is kept disabled.
|
||||||
|
- qcom,qpnp-ibb-ring-suppression-enable: A boolean property which upon set will
|
||||||
|
enable ring suppression for IBB
|
||||||
|
regulator. Otherwise, it is disabled.
|
||||||
|
- qcom,qpnp-ibb-limit-max-current-enable: A boolean property which upon set will
|
||||||
|
enforce maximum inductor current constraint
|
||||||
|
for IBB regulator. Otherwise, there is no
|
||||||
|
maximum current constraint.
|
||||||
|
- qcom,qpnp-ibb-use-default-voltage: A boolean property which upon set will
|
||||||
|
use the value specified in
|
||||||
|
qcom,qpnp-ibb-init-voltage property.
|
||||||
|
This will be used only if the bootloader
|
||||||
|
doesn't configure the output voltage
|
||||||
|
already. If it it not specified, then
|
||||||
|
output voltage can be configured to
|
||||||
|
any value in the allowed limit.
|
||||||
|
- qcom,output-voltage-one-pulse The expected voltage (in mV) of VDISN signal
|
||||||
|
on the first SWIRE pulse. This property
|
||||||
|
can be specified only if 'qpnp,swire-control'
|
||||||
|
is defined. The minimum and maximum values
|
||||||
|
are 1400mV and 7700mV.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
qcom,pmi8994@3 {
|
||||||
|
qpnp-labibb-regulator {
|
||||||
|
compatible = "qcom,qpnp-labibb-regulator";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
qpnp,qpnp-labibb-mode = "lcd";
|
||||||
|
|
||||||
|
lab_regulator: qcom,lab@de00 {
|
||||||
|
reg = <0xde00 0x100>;
|
||||||
|
reg-names = "lab";
|
||||||
|
regulator-name = "lab_reg";
|
||||||
|
|
||||||
|
regulator-min-microvolt = <4600000>;
|
||||||
|
regulator-max-microvolt = <6000000>;
|
||||||
|
|
||||||
|
qcom,qpnp-lab-min-voltage = <4600000>;
|
||||||
|
qcom,qpnp-lab-step-size = <100000>;
|
||||||
|
qcom,qpnp-lab-slew-rate = <5000>;
|
||||||
|
qcom,qpnp-lab-use-default-voltage;
|
||||||
|
qcom,qpnp-lab-init-voltage = <5500000>;
|
||||||
|
qcom,qpnp-lab-init-amoled-voltage = <4600000>;
|
||||||
|
qcom,qpnp-lab-init-lcd-voltage = <5500000>;
|
||||||
|
|
||||||
|
qcom,qpnp-lab-soft-start = <400>;
|
||||||
|
|
||||||
|
qcom,qpnp-lab-full-pull-down;
|
||||||
|
qcom,qpnp-lab-pull-down-enable;
|
||||||
|
qcom,qpnp-lab-switching-clock-frequency = <1600>;
|
||||||
|
qcom,qpnp-lab-limit-maximum-current = <800>;
|
||||||
|
qcom,qpnp-lab-limit-max-current-enable;
|
||||||
|
qcom,qpnp-lab-ps-threshold = <40>;
|
||||||
|
qcom,qpnp-lab-ps-enable;
|
||||||
|
qcom,qpnp-lab-nfet-size = <100>;
|
||||||
|
qcom,qpnp-lab-pfet-size = <100>;
|
||||||
|
qcom,qpnp-lab-max-precharge-time = <200>;
|
||||||
|
};
|
||||||
|
|
||||||
|
ibb_regulator: qcom,ibb@dc00 {
|
||||||
|
reg = <0xdc00 0x100>;
|
||||||
|
reg-names = "ibb_reg";
|
||||||
|
regulator-name = "ibb_reg";
|
||||||
|
|
||||||
|
regulator-min-microvolt = <4600000>;
|
||||||
|
regulator-max-microvolt = <6000000>;
|
||||||
|
|
||||||
|
qcom,qpnp-ibb-min-voltage = <1400000>;
|
||||||
|
qcom,qpnp-ibb-step-size = <100000>;
|
||||||
|
qcom,qpnp-ibb-slew-rate = <2000000>;
|
||||||
|
qcom,qpnp-ibb-use-default-voltage;
|
||||||
|
qcom,qpnp-ibb-init-voltage = <5500000>;
|
||||||
|
qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
|
||||||
|
qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
|
||||||
|
|
||||||
|
qcom,qpnp-ibb-soft-start = <400>;
|
||||||
|
|
||||||
|
qcom,qpnp-ibb-discharge-resistor = <300>;
|
||||||
|
qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
|
||||||
|
qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
|
||||||
|
qcom,qpnp-ibb-en-discharge;
|
||||||
|
|
||||||
|
qcom,qpnp-ibb-full-pull-down;
|
||||||
|
qcom,qpnp-ibb-pull-down-enable;
|
||||||
|
qcom,qpnp-ibb-switching-clock-frequency = <1480>;
|
||||||
|
qcom,qpnp-ibb-limit-maximum-current = <1550>;
|
||||||
|
qcom,qpnp-ibb-debounce-cycle = <16>;
|
||||||
|
qcom,qpnp-ibb-limit-max-current-enable;
|
||||||
|
qcom,qpnp-ibb-ps-enable;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
151
Documentation/devicetree/bindings/regulator/qpnp-regulator.txt
Normal file
151
Documentation/devicetree/bindings/regulator/qpnp-regulator.txt
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
Qualcomm QPNP Regulators
|
||||||
|
|
||||||
|
qpnp-regulator is a regulator driver which supports regulators inside of PMICs
|
||||||
|
that utilize the MSM SPMI implementation.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Must be "qcom,qpnp-regulator"
|
||||||
|
- reg: Specifies the SPMI address and size for this regulator device
|
||||||
|
Note, this is the only property which can be used within a
|
||||||
|
subnode.
|
||||||
|
- regulator-name: A string used as a descriptive name for regulator outputs
|
||||||
|
- parent-supply: phandle to the parent supply/regulator node
|
||||||
|
|
||||||
|
Required structure:
|
||||||
|
- A qcom,qpnp-regulator node must be a child of an spmi_device node.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- interrupts: List of interrupts used by the regulator.
|
||||||
|
- interrupt-names: List of strings defining the names of the
|
||||||
|
interrupts in the 'interrupts' property 1-to-1.
|
||||||
|
Supported values are "ocp" for voltage switch
|
||||||
|
type regulators. If an OCP interrupt is
|
||||||
|
specified, then the voltage switch will be
|
||||||
|
toggled off and back on when OCP triggers in
|
||||||
|
order to handle high in-rush current.
|
||||||
|
- qcom,system-load: Load in uA present on regulator that is not
|
||||||
|
captured by any consumer request
|
||||||
|
- qcom,enable-time: Time in us to delay after enabling the regulator
|
||||||
|
- qcom,auto-mode-enable: 1 = Enable automatic hardware selection of
|
||||||
|
regulator mode (HPM vs LPM); not available on
|
||||||
|
boost type regulators
|
||||||
|
0 = Disable auto mode selection
|
||||||
|
- qcom,bypass-mode-enable: 1 = Enable bypass mode for an LDO type regulator
|
||||||
|
so that it acts like a switch and simply outputs
|
||||||
|
its input voltage
|
||||||
|
0 = Do not enable bypass mode
|
||||||
|
- qcom,ocp-enable: 1 = Allow over current protection (OCP) to be
|
||||||
|
enabled for voltage switch type regulators so
|
||||||
|
that they latch off automatically when over
|
||||||
|
current is detected. OCP is enabled when in
|
||||||
|
HPM or auto mode.
|
||||||
|
0 = Disable OCP
|
||||||
|
- qcom,ocp-max-retries: Maximum number of times to try toggling a voltage
|
||||||
|
switch off and back on as a result of
|
||||||
|
consecutive over current events.
|
||||||
|
- qcom,ocp-retry-delay: Time to delay in milliseconds between each
|
||||||
|
voltage switch toggle after an over current
|
||||||
|
event takes place.
|
||||||
|
- qcom,pull-down-enable: 1 = Enable output pull down resistor when the
|
||||||
|
regulator is disabled
|
||||||
|
0 = Disable pull down resistor
|
||||||
|
- qcom,soft-start-enable: 1 = Enable soft start for LDO and voltage switch
|
||||||
|
type regulators so that output voltage slowly
|
||||||
|
ramps up when the regulator is enabled
|
||||||
|
0 = Disable soft start
|
||||||
|
- qcom,boost-current-limit: This property sets the current limit of boost
|
||||||
|
type regulators; supported values are:
|
||||||
|
0 = 300 mA
|
||||||
|
1 = 600 mA
|
||||||
|
2 = 900 mA
|
||||||
|
3 = 1200 mA
|
||||||
|
4 = 1500 mA
|
||||||
|
5 = 1800 mA
|
||||||
|
6 = 2100 mA
|
||||||
|
7 = 2400 mA
|
||||||
|
- qcom,pin-ctrl-enable: Bit mask specifying which hardware pins should be
|
||||||
|
used to enable the regulator, if any; supported
|
||||||
|
bits are:
|
||||||
|
0 = ignore all hardware enable signals
|
||||||
|
BIT(0) = follow HW0_EN signal
|
||||||
|
BIT(1) = follow HW1_EN signal
|
||||||
|
BIT(2) = follow HW2_EN signal
|
||||||
|
BIT(3) = follow HW3_EN signal
|
||||||
|
- qcom,pin-ctrl-hpm: Bit mask specifying which hardware pins should be
|
||||||
|
used to force the regulator into high power
|
||||||
|
mode, if any; supported bits are:
|
||||||
|
0 = ignore all hardware enable signals
|
||||||
|
BIT(0) = follow HW0_EN signal
|
||||||
|
BIT(1) = follow HW1_EN signal
|
||||||
|
BIT(2) = follow HW2_EN signal
|
||||||
|
BIT(3) = follow HW3_EN signal
|
||||||
|
BIT(4) = follow PMIC awake state
|
||||||
|
- qcom,vs-soft-start-strength: This property sets the soft start strength for
|
||||||
|
voltage switch type regulators; supported values
|
||||||
|
are:
|
||||||
|
0 = 0.05 uA
|
||||||
|
1 = 0.25 uA
|
||||||
|
2 = 0.55 uA
|
||||||
|
3 = 0.75 uA
|
||||||
|
- qcom,hpm-enable: 1 = Enable high power mode (HPM), also referred
|
||||||
|
to as NPM. HPM consumes more ground current
|
||||||
|
than LPM, but it can source significantly higher
|
||||||
|
load current. HPM is not available on boost
|
||||||
|
type regulators. For voltage switch type
|
||||||
|
regulators, HPM implies that over current
|
||||||
|
protection and soft start are active all the
|
||||||
|
time. This configuration can be overwritten
|
||||||
|
by changing the regulator's mode dynamically.
|
||||||
|
0 = Do not enable HPM
|
||||||
|
- qcom,force-type: Override the type and subtype register values. Useful for some
|
||||||
|
regulators that have invalid types advertised by the hardware.
|
||||||
|
The format is two unsigned integers of the form <type subtype>.
|
||||||
|
|
||||||
|
Note, if a given optional qcom,* binding is not present, then the qpnp-regulator
|
||||||
|
driver will leave that feature in the default hardware state.
|
||||||
|
|
||||||
|
All properties specified within the core regulator framework can also be used.
|
||||||
|
These bindings can be found in regulator.txt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
qcom,spmi@fc4c0000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
|
||||||
|
qcom,pm8941@1 {
|
||||||
|
reg = <0x1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
regulator@1400 {
|
||||||
|
regulator-name = "8941_s1";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
compatible = "qcom,qpnp-regulator";
|
||||||
|
reg = <0x1400 0x300>;
|
||||||
|
regulator-min-microvolt = <1300000>;
|
||||||
|
regulator-max-microvolt = <1400000>;
|
||||||
|
|
||||||
|
qcom,ctl@1400 {
|
||||||
|
reg = <0x1400 0x100>;
|
||||||
|
};
|
||||||
|
qcom,ps@1500 {
|
||||||
|
reg = <0x1500 0x100>;
|
||||||
|
};
|
||||||
|
qcom,freq@1600 {
|
||||||
|
reg = <0x1600 0x100>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
regulator@4000 {
|
||||||
|
regulator-name = "8941_l1";
|
||||||
|
reg = <0x4000 0x100>;
|
||||||
|
compatible = "qcom,qpnp-regulator";
|
||||||
|
regulator-min-microvolt = <1225000>;
|
||||||
|
regulator-max-microvolt = <1300000>;
|
||||||
|
qcom,pull-down-enable = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -15,9 +15,6 @@ Required properties :
|
||||||
This must be set to '1'.
|
This must be set to '1'.
|
||||||
- #size-cells: The number of cells dedicated to represent address
|
- #size-cells: The number of cells dedicated to represent address
|
||||||
space range of a peripheral. This must be set to '1'.
|
space range of a peripheral. This must be set to '1'.
|
||||||
- spmi-dev-container: This specifies that all the device nodes specified
|
|
||||||
within this node should have their resources
|
|
||||||
coalesced into a single spmi_device.
|
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- qcom,qpnp-rtc-write: This property enables/disables rtc write
|
- qcom,qpnp-rtc-write: This property enables/disables rtc write
|
||||||
|
@ -44,7 +41,6 @@ Required properties :
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
qcom,pm8941_rtc {
|
qcom,pm8941_rtc {
|
||||||
spmi-dev-container;
|
|
||||||
compatible = "qcom,qpnp-rtc";
|
compatible = "qcom,qpnp-rtc";
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <1>;
|
#size-cells = <1>;
|
||||||
|
|
|
@ -205,3 +205,5 @@ CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
|
||||||
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
|
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
|
||||||
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
|
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
|
||||||
CONFIG_CRYPTO_CRC32_ARM64=y
|
CONFIG_CRYPTO_CRC32_ARM64=y
|
||||||
|
CONFIG_SPMI=y
|
||||||
|
CONFIG_MFD_SPMI_PMIC=y
|
||||||
|
|
|
@ -13,8 +13,7 @@ menuconfig BIF
|
||||||
|
|
||||||
if BIF
|
if BIF
|
||||||
config BIF_QPNP
|
config BIF_QPNP
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
tristate "Qualcomm QPNP BIF support"
|
tristate "Qualcomm QPNP BIF support"
|
||||||
help
|
help
|
||||||
This driver supports the QPNP BSI peripheral found inside of Qualcomm
|
This driver supports the QPNP BSI peripheral found inside of Qualcomm
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -17,12 +17,14 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/bif/driver.h>
|
#include <linux/bif/driver.h>
|
||||||
#include <linux/qpnp/qpnp-adc.h>
|
#include <linux/qpnp/qpnp-adc.h>
|
||||||
|
@ -41,7 +43,8 @@ enum qpnp_bsi_com_mode {
|
||||||
|
|
||||||
struct qpnp_bsi_chip {
|
struct qpnp_bsi_chip {
|
||||||
struct bif_ctrl_desc bdesc;
|
struct bif_ctrl_desc bdesc;
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
struct bif_ctrl_dev *bdev;
|
struct bif_ctrl_dev *bdev;
|
||||||
struct work_struct slave_irq_work;
|
struct work_struct slave_irq_work;
|
||||||
u16 base_addr;
|
u16 base_addr;
|
||||||
|
@ -202,11 +205,13 @@ static inline int qpnp_bsi_read(struct qpnp_bsi_chip *chip, u16 addr, u8 *buf,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
|
rc = regmap_bulk_read(chip->regmap, chip->base_addr + addr, buf, len);
|
||||||
chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
__func__, chip->spmi_dev->sid, chip->base_addr + addr,
|
"%s: regmap_bulk_readl failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
||||||
|
__func__,
|
||||||
|
to_spmi_device(chip->pdev->dev.parent)->usid,
|
||||||
|
chip->base_addr + addr,
|
||||||
len, rc);
|
len, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -217,12 +222,14 @@ static inline int qpnp_bsi_write(struct qpnp_bsi_chip *chip, u16 addr, u8 *buf,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_bulk_write(chip->regmap, chip->base_addr + addr, buf, len);
|
||||||
chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
|
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
__func__, chip->spmi_dev->sid, chip->base_addr + addr,
|
"%s: regmap_bulk_write failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
||||||
|
__func__,
|
||||||
|
to_spmi_device(chip->pdev->dev.parent)->usid,
|
||||||
|
chip->base_addr + addr,
|
||||||
len, rc);
|
len, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -278,14 +285,15 @@ static int qpnp_bsi_rx_tx_config(struct qpnp_bsi_chip *chip,
|
||||||
buf[1] = QPNP_BSI_TX_DISABLE | QPNP_BSI_RX_DISABLE;
|
buf[1] = QPNP_BSI_TX_DISABLE | QPNP_BSI_RX_DISABLE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: invalid state=%d\n",
|
dev_err(&chip->pdev->dev, "%s: invalid state=%d\n",
|
||||||
__func__, state);
|
__func__, state);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -403,7 +411,8 @@ static int qpnp_bsi_clear_bsi_error(struct qpnp_bsi_chip *chip)
|
||||||
|
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_read() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -422,7 +431,8 @@ static int qpnp_bsi_clear_bsi_error(struct qpnp_bsi_chip *chip)
|
||||||
reg = QPNP_BSI_BSI_ERROR_CLEAR;
|
reg = QPNP_BSI_BSI_ERROR_CLEAR;
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_CLEAR_ERROR, ®, 1);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_CLEAR_ERROR, ®, 1);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +446,8 @@ static int qpnp_bsi_get_bsi_error(struct qpnp_bsi_chip *chip)
|
||||||
|
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_read() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -451,7 +462,8 @@ static int qpnp_bsi_wait_for_tx(struct qpnp_bsi_chip *chip, int timeout)
|
||||||
/* Wait for TX or ERR IRQ. */
|
/* Wait for TX or ERR IRQ. */
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
|
if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction error occurred, BSI error=%d\n",
|
||||||
__func__, qpnp_bsi_get_bsi_error(chip));
|
__func__, qpnp_bsi_get_bsi_error(chip));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -465,7 +477,8 @@ static int qpnp_bsi_wait_for_tx(struct qpnp_bsi_chip *chip, int timeout)
|
||||||
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, no interrupts received, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction timed out, no interrupts received, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -491,7 +504,8 @@ static int qpnp_bsi_issue_transaction(struct qpnp_bsi_chip *chip,
|
||||||
/* Write the TX_DATA bytes and initiate the transaction. */
|
/* Write the TX_DATA bytes and initiate the transaction. */
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_DATA_LOW, buf, 4);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_DATA_LOW, buf, 4);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -520,7 +534,8 @@ static int qpnp_bsi_wait_for_rx(struct qpnp_bsi_chip *chip, int timeout)
|
||||||
/* Wait for RX IRQ to indicate that data is ready to read. */
|
/* Wait for RX IRQ to indicate that data is ready to read. */
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
|
if (qpnp_bsi_check_irq(chip, QPNP_BSI_IRQ_ERR)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction error occurred, BSI error=%d\n",
|
||||||
__func__, qpnp_bsi_get_bsi_error(chip));
|
__func__, qpnp_bsi_get_bsi_error(chip));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -548,7 +563,8 @@ static int qpnp_bsi_bus_transaction(struct bif_ctrl_dev *bdev, int transaction,
|
||||||
|
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -582,7 +598,8 @@ static int qpnp_bsi_bus_transaction_query(struct bif_ctrl_dev *bdev,
|
||||||
|
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -628,7 +645,8 @@ static int qpnp_bsi_bus_transaction_read(struct bif_ctrl_dev *bdev,
|
||||||
|
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -658,7 +676,8 @@ static int qpnp_bsi_bus_transaction_read(struct bif_ctrl_dev *bdev,
|
||||||
* to provide silent operation when checking if a slave
|
* to provide silent operation when checking if a slave
|
||||||
* is selected using the transaction query bus command.
|
* is selected using the transaction query bus command.
|
||||||
*/
|
*/
|
||||||
dev_dbg(&chip->spmi_dev->dev, "%s: transaction timed out, no interrupts received, rc=%d\n",
|
dev_dbg(&chip->pdev->dev,
|
||||||
|
"%s: transaction timed out, no interrupts received, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -667,14 +686,16 @@ static int qpnp_bsi_bus_transaction_read(struct bif_ctrl_dev *bdev,
|
||||||
/* Read the RX_DATA bytes. */
|
/* Read the RX_DATA bytes. */
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf, 3);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf, 3);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_read() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[2] & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
|
if (buf[2] & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: unexpected loopback data read, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: unexpected loopback data read, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -702,13 +723,15 @@ static int qpnp_bsi_wait_for_rx_data(struct qpnp_bsi_chip *chip)
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg & QPNP_BSI_STATUS_ERROR) {
|
if (reg & QPNP_BSI_STATUS_ERROR) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction error occurred, BSI error=%d\n",
|
||||||
__func__, qpnp_bsi_get_bsi_error(chip));
|
__func__, qpnp_bsi_get_bsi_error(chip));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -723,7 +746,8 @@ static int qpnp_bsi_wait_for_rx_data(struct qpnp_bsi_chip *chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, RX_FLOW_STATUS never set to 1, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction timed out, RX_FLOW_STATUS never set to 1, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -745,13 +769,15 @@ static int qpnp_bsi_wait_for_tx_go(struct qpnp_bsi_chip *chip)
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg & QPNP_BSI_STATUS_ERROR) {
|
if (reg & QPNP_BSI_STATUS_ERROR) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction error occurred, BSI error=%d\n",
|
||||||
__func__, qpnp_bsi_get_bsi_error(chip));
|
__func__, qpnp_bsi_get_bsi_error(chip));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -766,7 +792,8 @@ static int qpnp_bsi_wait_for_tx_go(struct qpnp_bsi_chip *chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, TX_GO_STATUS never set to 0, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction timed out, TX_GO_STATUS never set to 0, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -788,13 +815,15 @@ static int qpnp_bsi_wait_for_tx_idle(struct qpnp_bsi_chip *chip)
|
||||||
while (timeout > 0) {
|
while (timeout > 0) {
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg & QPNP_BSI_STATUS_ERROR) {
|
if (reg & QPNP_BSI_STATUS_ERROR) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction error occurred, BSI error=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction error occurred, BSI error=%d\n",
|
||||||
__func__, qpnp_bsi_get_bsi_error(chip));
|
__func__, qpnp_bsi_get_bsi_error(chip));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -809,7 +838,8 @@ static int qpnp_bsi_wait_for_tx_idle(struct qpnp_bsi_chip *chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: transaction timed out, TX_BUSY never set to 0, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: transaction timed out, TX_BUSY never set to 0, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -872,23 +902,27 @@ static int qpnp_bsi_validate_rx_data(struct qpnp_bsi_chip *chip, int response,
|
||||||
int err = -EIO;
|
int err = -EIO;
|
||||||
|
|
||||||
if (rx2_data & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
|
if (rx2_data & QPNP_BSI_RX_SRC_LOOPBACK_FLAG) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: unexpected loopback data read, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: unexpected loopback data read, rc=%d\n",
|
||||||
__func__, err);
|
__func__, err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(response & BIF_SLAVE_RD_ACK)) {
|
if (!(response & BIF_SLAVE_RD_ACK)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: BIF register read error=0x%02X\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: BIF register read error=0x%02X\n",
|
||||||
__func__, response & BIF_SLAVE_RD_ERR);
|
__func__, response & BIF_SLAVE_RD_ERR);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_word && !(response & BIF_SLAVE_RD_EOT)) {
|
if (last_word && !(response & BIF_SLAVE_RD_EOT)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: BIF register read error, last RD packet has EOT=0\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: BIF register read error, last RD packet has EOT=0\n",
|
||||||
__func__);
|
__func__);
|
||||||
return err;
|
return err;
|
||||||
} else if (!last_word && (response & BIF_SLAVE_RD_EOT)) {
|
} else if (!last_word && (response & BIF_SLAVE_RD_EOT)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: BIF register read error, RD packet other than last has EOT=1\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: BIF register read error, RD packet other than last has EOT=1\n",
|
||||||
__func__);
|
__func__);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -910,7 +944,8 @@ static int qpnp_bsi_read_slave_registers(struct bif_ctrl_dev *bdev, u16 addr,
|
||||||
|
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -957,7 +992,8 @@ static int qpnp_bsi_read_slave_registers(struct bif_ctrl_dev *bdev, u16 addr,
|
||||||
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf,
|
rc = qpnp_bsi_read(chip, QPNP_BSI_REG_RX_DATA_LOW, buf,
|
||||||
3);
|
3);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_read() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_read() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto burst_err;
|
goto burst_err;
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1041,8 @@ static int qpnp_bsi_write_slave_registers(struct bif_ctrl_dev *bdev, u16 addr,
|
||||||
|
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1083,7 +1120,8 @@ static int qpnp_bsi_bus_set_interrupt_mode(struct bif_ctrl_dev *bdev)
|
||||||
*/
|
*/
|
||||||
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
rc = qpnp_bsi_set_bus_state(bdev, BIF_BUS_STATE_ACTIVE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to set bus state, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to set bus state, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1138,7 +1176,8 @@ static int qpnp_bsi_bus_set_active_mode(struct bif_ctrl_dev *bdev,
|
||||||
|
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_MODE, buf, 2);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1147,7 +1186,8 @@ static int qpnp_bsi_bus_set_active_mode(struct bif_ctrl_dev *bdev,
|
||||||
/* Initiate BCL low pulse. */
|
/* Initiate BCL low pulse. */
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_CTRL, buf, 1);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TX_CTRL, buf, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1235,8 @@ static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state)
|
||||||
reg = QPNP_BSI_ENABLE;
|
reg = QPNP_BSI_ENABLE;
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, ®, 1);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1207,25 +1248,29 @@ static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state)
|
||||||
reg = QPNP_BSI_DISABLE;
|
reg = QPNP_BSI_DISABLE;
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, ®, 1);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, ®, 1);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
break;
|
break;
|
||||||
case BIF_BUS_STATE_POWER_DOWN:
|
case BIF_BUS_STATE_POWER_DOWN:
|
||||||
rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_PDWN);
|
rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_PDWN);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to enable power down mode, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to enable power down mode, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
break;
|
break;
|
||||||
case BIF_BUS_STATE_STANDBY:
|
case BIF_BUS_STATE_STANDBY:
|
||||||
rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_STBY);
|
rc = qpnp_bsi_bus_transaction(bdev, BIF_TRANS_BC, BIF_CMD_STBY);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to enable standby mode, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to enable standby mode, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
break;
|
break;
|
||||||
case BIF_BUS_STATE_ACTIVE:
|
case BIF_BUS_STATE_ACTIVE:
|
||||||
rc = qpnp_bsi_bus_set_active_mode(bdev, chip->state);
|
rc = qpnp_bsi_bus_set_active_mode(bdev, chip->state);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to enable active mode, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to enable active mode, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
break;
|
break;
|
||||||
case BIF_BUS_STATE_INTERRUPT:
|
case BIF_BUS_STATE_INTERRUPT:
|
||||||
|
@ -1236,7 +1281,8 @@ static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state)
|
||||||
*/
|
*/
|
||||||
rc = qpnp_bsi_bus_set_interrupt_mode(bdev);
|
rc = qpnp_bsi_bus_set_interrupt_mode(bdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: failed to enable interrupt mode, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: failed to enable interrupt mode, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
} else if (chip->state == BIF_BUS_STATE_ACTIVE) {
|
} else if (chip->state == BIF_BUS_STATE_ACTIVE) {
|
||||||
/*
|
/*
|
||||||
|
@ -1249,7 +1295,7 @@ static int qpnp_bsi_set_bus_state(struct bif_ctrl_dev *bdev, int state)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: invalid state=%d\n",
|
dev_err(&chip->pdev->dev, "%s: invalid state=%d\n",
|
||||||
__func__, state);
|
__func__, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1329,7 +1375,8 @@ static int qpnp_bsi_set_tau_bif(struct qpnp_bsi_chip *chip, int period_ns)
|
||||||
reg = chip->tau_sampling_mask | idx;
|
reg = chip->tau_sampling_mask | idx;
|
||||||
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TAU_CONFIG, ®, 1);
|
rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TAU_CONFIG, ®, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_bsi_write() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_bsi_write() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1361,7 +1408,8 @@ static int qpnp_bsi_get_battery_rid(struct bif_ctrl_dev *bdev)
|
||||||
s64 temp;
|
s64 temp;
|
||||||
|
|
||||||
if (chip->batt_id_adc_channel >= ADC_MAX_NUM) {
|
if (chip->batt_id_adc_channel >= ADC_MAX_NUM) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: no ADC channel specified for Rid measurement\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: no ADC channel specified for Rid measurement\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -1382,7 +1430,8 @@ static int qpnp_bsi_get_battery_rid(struct bif_ctrl_dev *bdev)
|
||||||
rid_ohm = temp;
|
rid_ohm = temp;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_vadc_read(%d) failed, rc=%d\n",
|
||||||
__func__, chip->batt_id_adc_channel, rc);
|
__func__, chip->batt_id_adc_channel, rc);
|
||||||
rid_ohm = rc;
|
rid_ohm = rc;
|
||||||
}
|
}
|
||||||
|
@ -1399,18 +1448,18 @@ static int qpnp_bsi_get_battery_rid(struct bif_ctrl_dev *bdev)
|
||||||
static int qpnp_bsi_get_battery_presence(struct bif_ctrl_dev *bdev)
|
static int qpnp_bsi_get_battery_presence(struct bif_ctrl_dev *bdev)
|
||||||
{
|
{
|
||||||
struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
|
struct qpnp_bsi_chip *chip = bdev_get_drvdata(bdev);
|
||||||
u8 reg = 0x00;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_read(chip->regmap, chip->batt_id_stat_addr, &val);
|
||||||
chip->batt_id_stat_addr, ®, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: regmap_bulk_readl failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!(reg & QPNP_SMBB_BAT_IF_BATT_PRES_MASK);
|
return !!(val & QPNP_SMBB_BAT_IF_BATT_PRES_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bif_ctrl_ops qpnp_bsi_ops = {
|
static struct bif_ctrl_ops qpnp_bsi_ops = {
|
||||||
|
@ -1429,11 +1478,11 @@ static struct bif_ctrl_ops qpnp_bsi_ops = {
|
||||||
|
|
||||||
/* Load all BSI properties from device tree. */
|
/* Load all BSI properties from device tree. */
|
||||||
static int qpnp_bsi_parse_dt(struct qpnp_bsi_chip *chip,
|
static int qpnp_bsi_parse_dt(struct qpnp_bsi_chip *chip,
|
||||||
struct spmi_device *spmi)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &spmi->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
int rc, temp;
|
int rc, temp;
|
||||||
|
|
||||||
chip->batt_id_adc_channel = ADC_MAX_NUM;
|
chip->batt_id_adc_channel = ADC_MAX_NUM;
|
||||||
|
@ -1466,27 +1515,28 @@ static int qpnp_bsi_parse_dt(struct qpnp_bsi_chip *chip,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM, "bsi-base");
|
rc = of_property_read_u32(pdev->dev.of_node, "bsi-base", &base);
|
||||||
if (!res) {
|
if (rc < 0) {
|
||||||
dev_err(dev, "%s: node is missing BSI base address\n",
|
dev_err(&pdev->dev,
|
||||||
__func__);
|
"Couldn't find bsi-base in node = %s rc = %d\n",
|
||||||
return -EINVAL;
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
chip->base_addr = res->start;
|
chip->base_addr = base;
|
||||||
|
|
||||||
res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
rc = of_property_read_u32(pdev->dev.of_node, "batt-id-status", &base);
|
||||||
"batt-id-status");
|
if (rc < 0) {
|
||||||
if (!res) {
|
dev_err(&pdev->dev,
|
||||||
dev_err(dev, "%s: node is missing BATT_ID status address\n",
|
"Couldn't find batt-if-status in node = %s rc = %d\n",
|
||||||
__func__);
|
pdev->dev.of_node->full_name, rc);
|
||||||
return -EINVAL;
|
return rc;
|
||||||
}
|
}
|
||||||
chip->batt_id_stat_addr = res->start;
|
chip->batt_id_stat_addr = base;
|
||||||
|
|
||||||
chip->bdesc.name = spmi_get_primary_dev_name(spmi);
|
chip->bdesc.name = dev_name(&pdev->dev);
|
||||||
if (!chip->bdesc.name) {
|
if (!chip->bdesc.name) {
|
||||||
dev_err(dev, "%s: label binding undefined for node %s\n",
|
dev_err(dev, "%s: label binding undefined for node %s\n",
|
||||||
__func__, spmi->dev.of_node->full_name);
|
__func__, pdev->dev.of_node->full_name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1524,26 +1574,25 @@ static int qpnp_bsi_parse_dt(struct qpnp_bsi_chip *chip,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->irq[QPNP_BSI_IRQ_ERR] = spmi_get_irq_byname(spmi, NULL, "err");
|
chip->irq[QPNP_BSI_IRQ_ERR] = platform_get_irq_byname(pdev, "err");
|
||||||
if (chip->irq[QPNP_BSI_IRQ_ERR] < 0) {
|
if (chip->irq[QPNP_BSI_IRQ_ERR] < 0) {
|
||||||
dev_err(dev, "%s: node is missing err irq\n", __func__);
|
dev_err(dev, "%s: node is missing err irq\n", __func__);
|
||||||
return chip->irq[QPNP_BSI_IRQ_ERR];
|
return chip->irq[QPNP_BSI_IRQ_ERR];
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->irq[QPNP_BSI_IRQ_RX] = spmi_get_irq_byname(spmi, NULL, "rx");
|
chip->irq[QPNP_BSI_IRQ_RX] = platform_get_irq_byname(pdev, "rx");
|
||||||
if (chip->irq[QPNP_BSI_IRQ_RX] < 0) {
|
if (chip->irq[QPNP_BSI_IRQ_RX] < 0) {
|
||||||
dev_err(dev, "%s: node is missing rx irq\n", __func__);
|
dev_err(dev, "%s: node is missing rx irq\n", __func__);
|
||||||
return chip->irq[QPNP_BSI_IRQ_RX];
|
return chip->irq[QPNP_BSI_IRQ_RX];
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->irq[QPNP_BSI_IRQ_TX] = spmi_get_irq_byname(spmi, NULL, "tx");
|
chip->irq[QPNP_BSI_IRQ_TX] = platform_get_irq_byname(pdev, "tx");
|
||||||
if (chip->irq[QPNP_BSI_IRQ_TX] < 0) {
|
if (chip->irq[QPNP_BSI_IRQ_TX] < 0) {
|
||||||
dev_err(dev, "%s: node is missing tx irq\n", __func__);
|
dev_err(dev, "%s: node is missing tx irq\n", __func__);
|
||||||
return chip->irq[QPNP_BSI_IRQ_TX];
|
return chip->irq[QPNP_BSI_IRQ_TX];
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->batt_present_irq = spmi_get_irq_byname(spmi, NULL,
|
chip->batt_present_irq = platform_get_irq_byname(pdev, "batt-present");
|
||||||
"batt-present");
|
|
||||||
if (chip->batt_present_irq < 0) {
|
if (chip->batt_present_irq < 0) {
|
||||||
dev_err(dev, "%s: node is missing batt-present irq\n",
|
dev_err(dev, "%s: node is missing batt-present irq\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
@ -1641,14 +1690,14 @@ static void qpnp_bsi_cleanup_irqs(struct qpnp_bsi_chip *chip)
|
||||||
irq_set_irq_wake(chip->batt_present_irq, 0);
|
irq_set_irq_wake(chip->batt_present_irq, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_bsi_probe(struct spmi_device *spmi)
|
static int qpnp_bsi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &spmi->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct qpnp_bsi_chip *chip;
|
struct qpnp_bsi_chip *chip;
|
||||||
int rc;
|
int rc;
|
||||||
u8 type[2];
|
u8 type[2];
|
||||||
|
|
||||||
if (!spmi->dev.of_node) {
|
if (!pdev->dev.of_node) {
|
||||||
dev_err(dev, "%s: device node missing\n", __func__);
|
dev_err(dev, "%s: device node missing\n", __func__);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -1658,8 +1707,13 @@ static int qpnp_bsi_probe(struct spmi_device *spmi)
|
||||||
dev_err(dev, "%s: Can't allocate qpnp_bsi\n", __func__);
|
dev_err(dev, "%s: Can't allocate qpnp_bsi\n", __func__);
|
||||||
return -ENOMEM;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
rc = qpnp_bsi_parse_dt(chip, spmi);
|
rc = qpnp_bsi_parse_dt(chip, pdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(dev, "%s: device tree parsing failed, rc=%d\n",
|
dev_err(dev, "%s: device tree parsing failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
@ -1675,7 +1729,7 @@ static int qpnp_bsi_probe(struct spmi_device *spmi)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->spmi_dev = spmi;
|
chip->pdev = pdev;
|
||||||
chip->bdesc.ops = &qpnp_bsi_ops;
|
chip->bdesc.ops = &qpnp_bsi_ops;
|
||||||
chip->state = BIF_BUS_STATE_MASTER_DISABLED;
|
chip->state = BIF_BUS_STATE_MASTER_DISABLED;
|
||||||
chip->com_mode = QPNP_BSI_COM_MODE_IRQ;
|
chip->com_mode = QPNP_BSI_COM_MODE_IRQ;
|
||||||
|
@ -1714,7 +1768,7 @@ static int qpnp_bsi_probe(struct spmi_device *spmi)
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->bdev = bif_ctrl_register(&chip->bdesc, dev, chip,
|
chip->bdev = bif_ctrl_register(&chip->bdesc, dev, chip,
|
||||||
spmi->dev.of_node);
|
pdev->dev.of_node);
|
||||||
if (IS_ERR(chip->bdev)) {
|
if (IS_ERR(chip->bdev)) {
|
||||||
rc = PTR_ERR(chip->bdev);
|
rc = PTR_ERR(chip->bdev);
|
||||||
dev_err(dev, "%s: bif_ctrl_register failed, rc=%d\n",
|
dev_err(dev, "%s: bif_ctrl_register failed, rc=%d\n",
|
||||||
|
@ -1731,10 +1785,11 @@ cleanup_irqs:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_bsi_remove(struct spmi_device *spmi)
|
static int qpnp_bsi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_bsi_chip *chip = dev_get_drvdata(&spmi->dev);
|
struct qpnp_bsi_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
|
||||||
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
if (chip) {
|
if (chip) {
|
||||||
bif_ctrl_unregister(chip->bdev);
|
bif_ctrl_unregister(chip->bdev);
|
||||||
|
@ -1749,13 +1804,13 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_bsi_id[] = {
|
static const struct platform_device_id qpnp_bsi_id[] = {
|
||||||
{ QPNP_BSI_DRIVER_NAME, 0 },
|
{ QPNP_BSI_DRIVER_NAME, 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, qpnp_bsi_id);
|
MODULE_DEVICE_TABLE(spmi, qpnp_bsi_id);
|
||||||
|
|
||||||
static struct spmi_driver qpnp_bsi_driver = {
|
static struct platform_driver qpnp_bsi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_BSI_DRIVER_NAME,
|
.name = QPNP_BSI_DRIVER_NAME,
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
|
@ -1768,12 +1823,12 @@ static struct spmi_driver qpnp_bsi_driver = {
|
||||||
|
|
||||||
static int __init qpnp_bsi_init(void)
|
static int __init qpnp_bsi_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_bsi_driver);
|
return platform_driver_register(&qpnp_bsi_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_bsi_exit(void)
|
static void __exit qpnp_bsi_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_bsi_driver);
|
platform_driver_unregister(&qpnp_bsi_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("QPNP PMIC BSI driver");
|
MODULE_DESCRIPTION("QPNP PMIC BSI driver");
|
||||||
|
|
|
@ -338,16 +338,14 @@ config GPIO_PXA
|
||||||
Say yes here to support the PXA GPIO device
|
Say yes here to support the PXA GPIO device
|
||||||
|
|
||||||
config GPIO_QPNP_PIN
|
config GPIO_QPNP_PIN
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
depends on MSM_QPNP_INT
|
|
||||||
tristate "Qualcomm QPNP gpio support"
|
tristate "Qualcomm QPNP gpio support"
|
||||||
help
|
help
|
||||||
Say 'y' here to include support for the Qualcomm QPNP gpio
|
Say 'y' here to include support for the Qualcomm QPNP gpio
|
||||||
driver. This driver supports Device Tree and allows a
|
driver. This driver supports Device Tree and allows a
|
||||||
device_node to be registered as a gpio-controller. It
|
device_node to be registered as a gpio-controller. It
|
||||||
does not handle gpio interrupts directly. That work is handled
|
does not handle gpio interrupts directly, they are handled
|
||||||
by CONFIG_MSM_QPNP_INT.
|
via the spmi arbiter interrupt driver.
|
||||||
|
|
||||||
config GPIO_QPNP_PIN_DEBUG
|
config GPIO_QPNP_PIN_DEBUG
|
||||||
depends on GPIO_QPNP_PIN
|
depends on GPIO_QPNP_PIN
|
||||||
|
|
|
@ -13,9 +13,11 @@
|
||||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -209,7 +211,8 @@ struct qpnp_pin_spec {
|
||||||
|
|
||||||
struct qpnp_pin_chip {
|
struct qpnp_pin_chip {
|
||||||
struct gpio_chip gpio_chip;
|
struct gpio_chip gpio_chip;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
struct qpnp_pin_spec **pmic_pins;
|
struct qpnp_pin_spec **pmic_pins;
|
||||||
struct qpnp_pin_spec **chip_gpios;
|
struct qpnp_pin_spec **chip_gpios;
|
||||||
uint32_t pmic_pin_lowest;
|
uint32_t pmic_pin_lowest;
|
||||||
|
@ -539,8 +542,8 @@ static int qpnp_pin_read_regs(struct qpnp_pin_chip *q_chip,
|
||||||
u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
|
u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
|
||||||
|
|
||||||
while (bytes_left > 0) {
|
while (bytes_left > 0) {
|
||||||
rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_bulk_read(q_chip->regmap, reg_addr, buf_p,
|
||||||
reg_addr, buf_p, bytes_left < 8 ? bytes_left : 8);
|
bytes_left < 8 ? bytes_left : 8);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
bytes_left -= 8;
|
bytes_left -= 8;
|
||||||
|
@ -559,8 +562,8 @@ static int qpnp_pin_write_regs(struct qpnp_pin_chip *q_chip,
|
||||||
u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
|
u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
|
||||||
|
|
||||||
while (bytes_left > 0) {
|
while (bytes_left > 0) {
|
||||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_bulk_write(q_chip->regmap, reg_addr, buf_p,
|
||||||
reg_addr, buf_p, bytes_left < 8 ? bytes_left : 8);
|
bytes_left < 8 ? bytes_left : 8);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
bytes_left -= 8;
|
bytes_left -= 8;
|
||||||
|
@ -574,7 +577,7 @@ static int qpnp_pin_cache_regs(struct qpnp_pin_chip *q_chip,
|
||||||
struct qpnp_pin_spec *q_spec)
|
struct qpnp_pin_spec *q_spec)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct device *dev = &q_chip->spmi->dev;
|
struct device *dev = &q_chip->pdev->dev;
|
||||||
|
|
||||||
rc = qpnp_pin_read_regs(q_chip, q_spec);
|
rc = qpnp_pin_read_regs(q_chip, q_spec);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -590,7 +593,7 @@ static int _qpnp_pin_config(struct qpnp_pin_chip *q_chip,
|
||||||
struct qpnp_pin_spec *q_spec,
|
struct qpnp_pin_spec *q_spec,
|
||||||
struct qpnp_pin_cfg *param)
|
struct qpnp_pin_cfg *param)
|
||||||
{
|
{
|
||||||
struct device *dev = &q_chip->spmi->dev;
|
struct device *dev = &q_chip->pdev->dev;
|
||||||
int rc;
|
int rc;
|
||||||
u8 shift, mask, *reg;
|
u8 shift, mask, *reg;
|
||||||
|
|
||||||
|
@ -699,7 +702,8 @@ static int _qpnp_pin_config(struct qpnp_pin_chip *q_chip,
|
||||||
|
|
||||||
rc = qpnp_pin_write_regs(q_chip, q_spec);
|
rc = qpnp_pin_write_regs(q_chip, q_spec);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&q_chip->spmi->dev, "%s: unable to write master enable\n",
|
dev_err(&q_chip->pdev->dev,
|
||||||
|
"%s: unable to write master enable\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto gpio_cfg;
|
goto gpio_cfg;
|
||||||
}
|
}
|
||||||
|
@ -789,14 +793,15 @@ static int qpnp_pin_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
|
||||||
|
|
||||||
/* call into irq_domain to get irq mapping */
|
/* call into irq_domain to get irq mapping */
|
||||||
oirq.np = q_chip->int_ctrl;
|
oirq.np = q_chip->int_ctrl;
|
||||||
oirq.args[0] = q_chip->spmi->sid;
|
oirq.args[0] = to_spmi_device(q_chip->pdev->dev.parent)->usid;
|
||||||
oirq.args[1] = (q_spec->offset >> 8) & 0xFF;
|
oirq.args[1] = (q_spec->offset >> 8) & 0xFF;
|
||||||
oirq.args[2] = 0;
|
oirq.args[2] = 0;
|
||||||
oirq.args_count = 3;
|
oirq.args[3] = IRQ_TYPE_NONE;
|
||||||
|
oirq.args_count = 4;
|
||||||
|
|
||||||
q_spec->irq = irq_create_of_mapping(&oirq);
|
q_spec->irq = irq_create_of_mapping(&oirq);
|
||||||
if (!q_spec->irq) {
|
if (!q_spec->irq) {
|
||||||
dev_err(&q_chip->spmi->dev, "%s: invalid irq for gpio %u\n",
|
dev_err(&q_chip->pdev->dev, "%s: invalid irq for gpio %u\n",
|
||||||
__func__, q_spec->pmic_pin);
|
__func__, q_spec->pmic_pin);
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -812,6 +817,7 @@ static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned offset)
|
||||||
struct qpnp_pin_spec *q_spec = NULL;
|
struct qpnp_pin_spec *q_spec = NULL;
|
||||||
u8 buf[1], en_mask;
|
u8 buf[1], en_mask;
|
||||||
u8 shift, mask, reg;
|
u8 shift, mask, reg;
|
||||||
|
int val;
|
||||||
|
|
||||||
if (WARN_ON(!q_chip))
|
if (WARN_ON(!q_chip))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -823,9 +829,9 @@ static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned offset)
|
||||||
/* gpio val is from RT status iff input is enabled */
|
/* gpio val is from RT status iff input is enabled */
|
||||||
if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
|
if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
|
||||||
== QPNP_PIN_MODE_DIG_IN) {
|
== QPNP_PIN_MODE_DIG_IN) {
|
||||||
rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_read(q_chip->regmap,
|
||||||
Q_REG_ADDR(q_spec, Q_REG_STATUS1),
|
Q_REG_ADDR(q_spec, Q_REG_STATUS1), &val);
|
||||||
&buf[0], 1);
|
buf[0] = (u8)val;
|
||||||
|
|
||||||
if (q_spec->type == Q_GPIO_TYPE && q_spec->dig_major_rev == 0)
|
if (q_spec->type == Q_GPIO_TYPE && q_spec->dig_major_rev == 0)
|
||||||
en_mask = Q_REG_STATUS1_GPIO_EN_REV0_MASK;
|
en_mask = Q_REG_STATUS1_GPIO_EN_REV0_MASK;
|
||||||
|
@ -839,7 +845,6 @@ static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned offset)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
return buf[0] & Q_REG_STATUS1_VAL_MASK;
|
return buf[0] & Q_REG_STATUS1_VAL_MASK;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (is_gpio_lv_mv(q_spec)) {
|
if (is_gpio_lv_mv(q_spec)) {
|
||||||
shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
|
shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
|
||||||
|
@ -882,10 +887,9 @@ static int __qpnp_pin_set(struct qpnp_pin_chip *q_chip,
|
||||||
|
|
||||||
q_reg_clr_set(reg, shift, mask, !!value);
|
q_reg_clr_set(reg, shift, mask, !!value);
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_write(q_chip->regmap, address, *reg);
|
||||||
address, reg, 1);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n",
|
dev_err(&q_chip->pdev->dev, "%s: spmi write failed\n",
|
||||||
__func__);
|
__func__);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -929,12 +933,10 @@ static int qpnp_pin_set_mode(struct qpnp_pin_chip *q_chip,
|
||||||
mask = Q_REG_MODE_SEL_MASK;
|
mask = Q_REG_MODE_SEL_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
|
q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL], shift, mask, mode);
|
||||||
shift, mask, mode);
|
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_write(q_chip->regmap, Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
|
||||||
Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
|
*&q_spec->regs[Q_REG_I_MODE_CTL]);
|
||||||
&q_spec->regs[Q_REG_I_MODE_CTL], 1);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1114,11 +1116,11 @@ static int qpnp_pin_apply_config(struct qpnp_pin_chip *q_chip,
|
||||||
|
|
||||||
static int qpnp_pin_free_chip(struct qpnp_pin_chip *q_chip)
|
static int qpnp_pin_free_chip(struct qpnp_pin_chip *q_chip)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = q_chip->spmi;
|
struct platform_device *pdev = q_chip->pdev;
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
|
||||||
if (q_chip->chip_gpios)
|
if (q_chip->chip_gpios)
|
||||||
for (i = 0; i < spmi->num_dev_node; i++)
|
for (i = 0; i < of_get_child_count(pdev->dev.of_node); i++)
|
||||||
kfree(q_chip->chip_gpios[i]);
|
kfree(q_chip->chip_gpios[i]);
|
||||||
|
|
||||||
mutex_lock(&qpnp_pin_chips_lock);
|
mutex_lock(&qpnp_pin_chips_lock);
|
||||||
|
@ -1293,9 +1295,8 @@ static int qpnp_pin_debugfs_set(void *data, u64 val)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
|
q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
|
||||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
rc = regmap_write(q_chip->regmap, Q_REG_ADDR(q_spec, cfg.addr),
|
||||||
Q_REG_ADDR(q_spec, cfg.addr),
|
*&q_spec->regs[cfg.idx]);
|
||||||
&q_spec->regs[cfg.idx], 1);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1327,8 +1328,8 @@ static struct qpnp_pin_debugfs_args dfs_args[] = {
|
||||||
|
|
||||||
static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
|
static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = q_chip->spmi;
|
struct platform_device *pdev = q_chip->pdev;
|
||||||
struct device *dev = &spmi->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct qpnp_pin_spec *q_spec;
|
struct qpnp_pin_spec *q_spec;
|
||||||
enum qpnp_pin_param_type *params;
|
enum qpnp_pin_param_type *params;
|
||||||
enum qpnp_pin_param_type type;
|
enum qpnp_pin_param_type type;
|
||||||
|
@ -1347,7 +1348,7 @@ static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
for (i = 0; i < of_get_child_count(pdev->dev.of_node); i++) {
|
||||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
|
q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
|
||||||
params = q_spec->params;
|
params = q_spec->params;
|
||||||
snprintf(pmic_pin, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_pin);
|
snprintf(pmic_pin, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_pin);
|
||||||
|
@ -1419,44 +1420,53 @@ static int qpnp_pin_is_valid_pin(struct qpnp_pin_spec *q_spec)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_pin_probe(struct spmi_device *spmi)
|
static int qpnp_pin_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_pin_chip *q_chip;
|
struct qpnp_pin_chip *q_chip;
|
||||||
struct qpnp_pin_spec *q_spec;
|
struct qpnp_pin_spec *q_spec;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
struct spmi_resource *d_node;
|
struct device_node *child;
|
||||||
int i, rc;
|
int i, rc;
|
||||||
u32 lowest_gpio = UINT_MAX, highest_gpio = 0;
|
u32 lowest_gpio = UINT_MAX, highest_gpio = 0;
|
||||||
u32 gpio;
|
u32 gpio;
|
||||||
char version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV + 1];
|
char version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV + 1];
|
||||||
const char *dev_name;
|
const char *pin_dev_name;
|
||||||
|
int num_dev_node;
|
||||||
|
|
||||||
dev_name = spmi_get_primary_dev_name(spmi);
|
pin_dev_name = dev_name(&pdev->dev);
|
||||||
if (!dev_name) {
|
if (!pin_dev_name) {
|
||||||
dev_err(&spmi->dev, "%s: label binding undefined for node %s\n",
|
dev_err(&pdev->dev,
|
||||||
__func__, spmi->dev.of_node->full_name);
|
"%s: label binding undefined for node %s\n",
|
||||||
|
__func__,
|
||||||
|
pdev->dev.of_node->full_name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
|
q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
|
||||||
if (!q_chip) {
|
if (!q_chip)
|
||||||
dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n",
|
|
||||||
__func__);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
q_chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!q_chip->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
q_chip->spmi = spmi;
|
q_chip->pdev = pdev;
|
||||||
dev_set_drvdata(&spmi->dev, q_chip);
|
dev_set_drvdata(&pdev->dev, q_chip);
|
||||||
|
|
||||||
mutex_lock(&qpnp_pin_chips_lock);
|
mutex_lock(&qpnp_pin_chips_lock);
|
||||||
list_add(&q_chip->chip_list, &qpnp_pin_chips);
|
list_add(&q_chip->chip_list, &qpnp_pin_chips);
|
||||||
mutex_unlock(&qpnp_pin_chips_lock);
|
mutex_unlock(&qpnp_pin_chips_lock);
|
||||||
|
|
||||||
/* first scan through nodes to find the range required for allocation */
|
/* first scan through nodes to find the range required for allocation */
|
||||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
i = 0;
|
||||||
rc = of_property_read_u32(spmi->dev_node[i].of_node,
|
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||||
"qcom,pin-num", &gpio);
|
if (!of_device_is_available(child))
|
||||||
|
continue;
|
||||||
|
rc = of_property_read_u32(child, "qcom,pin-num", &gpio);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: unable to get qcom,pin-num property\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: unable to get qcom,pin-num property\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
@ -1465,15 +1475,18 @@ static int qpnp_pin_probe(struct spmi_device *spmi)
|
||||||
lowest_gpio = gpio;
|
lowest_gpio = gpio;
|
||||||
if (gpio > highest_gpio)
|
if (gpio > highest_gpio)
|
||||||
highest_gpio = gpio;
|
highest_gpio = gpio;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
num_dev_node = i;
|
||||||
|
|
||||||
if (highest_gpio < lowest_gpio) {
|
if (highest_gpio < lowest_gpio) {
|
||||||
dev_err(&spmi->dev, "%s: no device nodes specified in topology\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: no device nodes specified in topology\n",
|
||||||
__func__);
|
__func__);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
} else if (lowest_gpio == 0) {
|
} else if (lowest_gpio == 0) {
|
||||||
dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n",
|
dev_err(&pdev->dev, "%s: 0 is not a valid PMIC GPIO\n",
|
||||||
__func__);
|
__func__);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
|
@ -1487,63 +1500,63 @@ static int qpnp_pin_probe(struct spmi_device *spmi)
|
||||||
(highest_gpio - lowest_gpio + 1),
|
(highest_gpio - lowest_gpio + 1),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_pin_spec *) *
|
q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_pin_spec *) *
|
||||||
spmi->num_dev_node, GFP_KERNEL);
|
num_dev_node,
|
||||||
|
GFP_KERNEL);
|
||||||
if (!q_chip->pmic_pins || !q_chip->chip_gpios) {
|
if (!q_chip->pmic_pins || !q_chip->chip_gpios) {
|
||||||
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
|
dev_err(&pdev->dev, "%s: unable to allocate memory\n",
|
||||||
__func__);
|
__func__);
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get interrupt controller device_node */
|
/* get interrupt controller device_node */
|
||||||
q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node);
|
q_chip->int_ctrl = of_irq_find_parent(pdev->dev.of_node);
|
||||||
if (!q_chip->int_ctrl) {
|
if (!q_chip->int_ctrl) {
|
||||||
dev_err(&spmi->dev, "%s: Can't find interrupt parent\n",
|
dev_err(&pdev->dev, "%s: Can't find interrupt parent\n",
|
||||||
__func__);
|
__func__);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
i = 0;
|
||||||
/* now scan through again and populate the lookup table */
|
/* now scan through again and populate the lookup table */
|
||||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||||
d_node = &spmi->dev_node[i];
|
if (!of_device_is_available(child))
|
||||||
res = spmi_get_resource(spmi, d_node, IORESOURCE_MEM, 0);
|
continue;
|
||||||
if (!res) {
|
rc = of_property_read_u32(child, "reg", &base);
|
||||||
dev_err(&spmi->dev, "%s: node %s is missing has no base address definition\n",
|
if (rc < 0) {
|
||||||
__func__, d_node->of_node->full_name);
|
dev_err(&pdev->dev,
|
||||||
rc = -EINVAL;
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
child->full_name, rc);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(d_node->of_node,
|
rc = of_property_read_u32(child, "qcom,pin-num", &gpio);
|
||||||
"qcom,pin-num", &gpio);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: unable to get qcom,pin-num property\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: unable to get qcom,pin-num property\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_spec = kzalloc(sizeof(struct qpnp_pin_spec),
|
q_spec = kzalloc(sizeof(struct qpnp_pin_spec), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!q_spec) {
|
if (!q_spec) {
|
||||||
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
|
|
||||||
__func__);
|
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_spec->slave = spmi->sid;
|
q_spec->slave = to_spmi_device(pdev->dev.parent)->usid;
|
||||||
q_spec->offset = res->start;
|
q_spec->offset = base;
|
||||||
q_spec->gpio_chip_idx = i;
|
q_spec->gpio_chip_idx = i;
|
||||||
q_spec->pmic_pin = gpio;
|
q_spec->pmic_pin = gpio;
|
||||||
q_spec->node = d_node->of_node;
|
q_spec->node = child;
|
||||||
q_spec->q_chip = q_chip;
|
q_spec->q_chip = q_chip;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, q_spec->slave,
|
rc = regmap_bulk_read(q_chip->regmap,
|
||||||
Q_REG_ADDR(q_spec, Q_REG_DIG_MAJOR_REV),
|
Q_REG_ADDR(q_spec, Q_REG_DIG_MAJOR_REV),
|
||||||
&version[0], ARRAY_SIZE(version));
|
&version[0],
|
||||||
|
ARRAY_SIZE(version));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: unable to read type regs\n",
|
dev_err(&pdev->dev, "%s: unable to read type regs\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
@ -1553,7 +1566,8 @@ static int qpnp_pin_probe(struct spmi_device *spmi)
|
||||||
q_spec->subtype = version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV];
|
q_spec->subtype = version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV];
|
||||||
|
|
||||||
if (!qpnp_pin_is_valid_pin(q_spec)) {
|
if (!qpnp_pin_is_valid_pin(q_spec)) {
|
||||||
dev_err(&spmi->dev, "%s: invalid pin type (type=0x%x subtype=0x%x)\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: invalid pin type (type=0x%x subtype=0x%x)\n",
|
||||||
__func__, q_spec->type, q_spec->subtype);
|
__func__, q_spec->type, q_spec->subtype);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
@ -1565,31 +1579,32 @@ static int qpnp_pin_probe(struct spmi_device *spmi)
|
||||||
/* initialize lookup table params */
|
/* initialize lookup table params */
|
||||||
qpnp_pmic_pin_set_spec(q_chip, gpio, q_spec);
|
qpnp_pmic_pin_set_spec(q_chip, gpio, q_spec);
|
||||||
qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
|
qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_chip->gpio_chip.base = -1;
|
q_chip->gpio_chip.base = -1;
|
||||||
q_chip->gpio_chip.ngpio = spmi->num_dev_node;
|
q_chip->gpio_chip.ngpio = num_dev_node;
|
||||||
q_chip->gpio_chip.label = dev_name;
|
q_chip->gpio_chip.label = pin_dev_name;
|
||||||
q_chip->gpio_chip.direction_input = qpnp_pin_direction_input;
|
q_chip->gpio_chip.direction_input = qpnp_pin_direction_input;
|
||||||
q_chip->gpio_chip.direction_output = qpnp_pin_direction_output;
|
q_chip->gpio_chip.direction_output = qpnp_pin_direction_output;
|
||||||
q_chip->gpio_chip.to_irq = qpnp_pin_to_irq;
|
q_chip->gpio_chip.to_irq = qpnp_pin_to_irq;
|
||||||
q_chip->gpio_chip.get = qpnp_pin_get;
|
q_chip->gpio_chip.get = qpnp_pin_get;
|
||||||
q_chip->gpio_chip.set = qpnp_pin_set;
|
q_chip->gpio_chip.set = qpnp_pin_set;
|
||||||
q_chip->gpio_chip.dev = &spmi->dev;
|
q_chip->gpio_chip.dev = &pdev->dev;
|
||||||
q_chip->gpio_chip.of_xlate = qpnp_pin_of_gpio_xlate;
|
q_chip->gpio_chip.of_xlate = qpnp_pin_of_gpio_xlate;
|
||||||
q_chip->gpio_chip.of_gpio_n_cells = 2;
|
q_chip->gpio_chip.of_gpio_n_cells = 2;
|
||||||
q_chip->gpio_chip.can_sleep = 0;
|
q_chip->gpio_chip.can_sleep = 0;
|
||||||
|
|
||||||
rc = gpiochip_add(&q_chip->gpio_chip);
|
rc = gpiochip_add(&q_chip->gpio_chip);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n",
|
dev_err(&pdev->dev, "%s: Can't add gpio chip, rc = %d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
q_chip->chip_registered = true;
|
q_chip->chip_registered = true;
|
||||||
/* now configure gpio config defaults if they exist */
|
/* now configure gpio config defaults if they exist */
|
||||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
for (i = 0; i < num_dev_node; i++) {
|
||||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
|
q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
|
||||||
if (WARN_ON(!q_spec)) {
|
if (WARN_ON(!q_spec)) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
|
@ -1605,13 +1620,14 @@ static int qpnp_pin_probe(struct spmi_device *spmi)
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n",
|
dev_dbg(&pdev->dev, "%s: gpio_chip registered between %d-%u\n",
|
||||||
__func__, q_chip->gpio_chip.base,
|
__func__, q_chip->gpio_chip.base,
|
||||||
(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
|
(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
|
||||||
|
|
||||||
rc = qpnp_pin_debugfs_create(q_chip);
|
rc = qpnp_pin_debugfs_create(q_chip);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: debugfs creation failed\n", __func__);
|
dev_err(&pdev->dev, "%s: debugfs creation failed\n",
|
||||||
|
__func__);
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1622,9 +1638,9 @@ err_probe:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_pin_remove(struct spmi_device *spmi)
|
static int qpnp_pin_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_pin_chip *q_chip = dev_get_drvdata(&spmi->dev);
|
struct qpnp_pin_chip *q_chip = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
debugfs_remove_recursive(q_chip->dfs_dir);
|
debugfs_remove_recursive(q_chip->dfs_dir);
|
||||||
|
|
||||||
|
@ -1637,15 +1653,15 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_pin_id[] = {
|
static const struct platform_device_id qpnp_pin_id[] = {
|
||||||
{ "qcom,qpnp-pin", 0 },
|
{ "qcom,qpnp-pin", 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, qpnp_pin_id);
|
MODULE_DEVICE_TABLE(spmi, qpnp_pin_id);
|
||||||
|
|
||||||
static struct spmi_driver qpnp_pin_driver = {
|
static struct platform_driver qpnp_pin_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-pin",
|
.name = "qcom,qpnp-pin",
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
},
|
},
|
||||||
.probe = qpnp_pin_probe,
|
.probe = qpnp_pin_probe,
|
||||||
|
@ -1661,7 +1677,7 @@ static int __init qpnp_pin_init(void)
|
||||||
pr_err("Cannot register top level debugfs directory\n");
|
pr_err("Cannot register top level debugfs directory\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return spmi_driver_register(&qpnp_pin_driver);
|
return platform_driver_register(&qpnp_pin_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_pin_exit(void)
|
static void __exit qpnp_pin_exit(void)
|
||||||
|
@ -1669,7 +1685,7 @@ static void __exit qpnp_pin_exit(void)
|
||||||
#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
|
#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
|
||||||
debugfs_remove_recursive(driver_dfs_dir);
|
debugfs_remove_recursive(driver_dfs_dir);
|
||||||
#endif
|
#endif
|
||||||
spmi_driver_unregister(&qpnp_pin_driver);
|
platform_driver_unregister(&qpnp_pin_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("QPNP PMIC gpio driver");
|
MODULE_DESCRIPTION("QPNP PMIC gpio driver");
|
||||||
|
|
|
@ -1202,7 +1202,7 @@ config SENSORS_PCF8591
|
||||||
|
|
||||||
config SENSORS_QPNP_ADC_VOLTAGE
|
config SENSORS_QPNP_ADC_VOLTAGE
|
||||||
tristate "Support for Qualcomm QPNP Voltage ADC"
|
tristate "Support for Qualcomm QPNP Voltage ADC"
|
||||||
depends on MSM_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
This is the VADC arbiter driver for Qualcomm QPNP ADC Chip.
|
This is the VADC arbiter driver for Qualcomm QPNP ADC Chip.
|
||||||
|
|
||||||
|
@ -1212,7 +1212,7 @@ config SENSORS_QPNP_ADC_VOLTAGE
|
||||||
|
|
||||||
config SENSORS_QPNP_ADC_CURRENT
|
config SENSORS_QPNP_ADC_CURRENT
|
||||||
tristate "Support for Qualcomm QPNP current ADC"
|
tristate "Support for Qualcomm QPNP current ADC"
|
||||||
depends on MSM_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
This is the IADC driver for Qualcomm QPNP ADC Chip.
|
This is the IADC driver for Qualcomm QPNP ADC Chip.
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
@ -1771,11 +1772,11 @@ int qpnp_adc_get_revid_version(struct device *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qpnp_adc_get_revid_version);
|
EXPORT_SYMBOL(qpnp_adc_get_revid_version);
|
||||||
|
|
||||||
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
|
||||||
struct qpnp_adc_drv *adc_qpnp)
|
struct qpnp_adc_drv *adc_qpnp)
|
||||||
{
|
{
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct qpnp_adc_amux *adc_channel_list;
|
struct qpnp_adc_amux *adc_channel_list;
|
||||||
struct qpnp_adc_properties *adc_prop;
|
struct qpnp_adc_properties *adc_prop;
|
||||||
|
@ -1794,24 +1795,25 @@ int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
adc_qpnp->spmi = spmi;
|
adc_qpnp->pdev = pdev;
|
||||||
|
|
||||||
adc_prop = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_properties),
|
adc_prop = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct qpnp_adc_properties),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!adc_prop)
|
if (!adc_prop)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
adc_channel_list = devm_kzalloc(&spmi->dev,
|
adc_channel_list = devm_kzalloc(&pdev->dev,
|
||||||
((sizeof(struct qpnp_adc_amux)) * count_adc_channel_list),
|
((sizeof(struct qpnp_adc_amux)) * count_adc_channel_list),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!adc_channel_list)
|
if (!adc_channel_list)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
amux_prop = devm_kzalloc(&spmi->dev,
|
amux_prop = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(struct qpnp_adc_amux_properties) +
|
sizeof(struct qpnp_adc_amux_properties) +
|
||||||
sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
|
sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
|
||||||
if (!amux_prop) {
|
if (!amux_prop) {
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1928,18 +1930,20 @@ int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
||||||
adc_qpnp->adc_prop = adc_prop;
|
adc_qpnp->adc_prop = adc_prop;
|
||||||
|
|
||||||
/* Get the peripheral address */
|
/* Get the peripheral address */
|
||||||
res = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
if (!res) {
|
if (rc < 0) {
|
||||||
pr_err("No base address definition\n");
|
dev_err(&pdev->dev,
|
||||||
return -EINVAL;
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
adc_qpnp->slave = spmi->sid;
|
adc_qpnp->slave = to_spmi_device(pdev->dev.parent)->usid;
|
||||||
adc_qpnp->offset = res->start;
|
adc_qpnp->offset = base;
|
||||||
|
|
||||||
/* Register the ADC peripheral interrupt */
|
/* Register the ADC peripheral interrupt */
|
||||||
adc_qpnp->adc_irq_eoc = spmi_get_irq_byname(spmi, NULL,
|
adc_qpnp->adc_irq_eoc = platform_get_irq_byname(pdev,
|
||||||
"eoc-int-en-set");
|
"eoc-int-en-set");
|
||||||
if (adc_qpnp->adc_irq_eoc < 0) {
|
if (adc_qpnp->adc_irq_eoc < 0) {
|
||||||
pr_err("Invalid irq\n");
|
pr_err("Invalid irq\n");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
@ -1948,8 +1952,7 @@ int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
||||||
init_completion(&adc_qpnp->adc_rslt_completion);
|
init_completion(&adc_qpnp->adc_rslt_completion);
|
||||||
|
|
||||||
if (of_get_property(node, "hkadc_ldo-supply", NULL)) {
|
if (of_get_property(node, "hkadc_ldo-supply", NULL)) {
|
||||||
adc_qpnp->hkadc_ldo = regulator_get(&spmi->dev,
|
adc_qpnp->hkadc_ldo = regulator_get(&pdev->dev, "hkadc_ldo");
|
||||||
"hkadc_ldo");
|
|
||||||
if (IS_ERR(adc_qpnp->hkadc_ldo)) {
|
if (IS_ERR(adc_qpnp->hkadc_ldo)) {
|
||||||
pr_err("hkadc_ldo-supply node not found\n");
|
pr_err("hkadc_ldo-supply node not found\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1972,7 +1975,7 @@ int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_get_property(node, "hkadc_ok-supply", NULL)) {
|
if (of_get_property(node, "hkadc_ok-supply", NULL)) {
|
||||||
adc_qpnp->hkadc_ldo_ok = regulator_get(&spmi->dev,
|
adc_qpnp->hkadc_ldo_ok = regulator_get(&pdev->dev,
|
||||||
"hkadc_ok");
|
"hkadc_ok");
|
||||||
if (IS_ERR(adc_qpnp->hkadc_ldo_ok)) {
|
if (IS_ERR(adc_qpnp->hkadc_ldo_ok)) {
|
||||||
pr_err("hkadc_ok node not found\n");
|
pr_err("hkadc_ok node not found\n");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -13,6 +13,7 @@
|
||||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#ifdef CONFIG_WAKELOCK
|
#ifdef CONFIG_WAKELOCK
|
||||||
#include <linux/wakelock.h>
|
#include <linux/wakelock.h>
|
||||||
|
@ -184,13 +186,14 @@ static int32_t qpnp_iadc_read_reg(struct qpnp_iadc_chip *iadc,
|
||||||
uint32_t reg, u8 *data)
|
uint32_t reg, u8 *data)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
|
rc = regmap_read(iadc->adc->regmap, (iadc->adc->offset + reg), &val);
|
||||||
(iadc->adc->offset + reg), data, 1);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
pr_err("qpnp iadc read reg %d failed with %d\n", reg, rc);
|
pr_err("qpnp iadc read reg %d failed with %d\n", reg, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
*data = (u8)val;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -202,8 +205,7 @@ static int32_t qpnp_iadc_write_reg(struct qpnp_iadc_chip *iadc,
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
|
||||||
buf = &data;
|
buf = &data;
|
||||||
rc = spmi_ext_register_writel(iadc->adc->spmi->ctrl, iadc->adc->slave,
|
rc = regmap_write(iadc->adc->regmap, (iadc->adc->offset + reg), *buf);
|
||||||
(iadc->adc->offset + reg), buf, 1);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
pr_err("qpnp iadc write reg %d failed with %d\n", reg, rc);
|
pr_err("qpnp iadc write reg %d failed with %d\n", reg, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -649,7 +651,8 @@ EXPORT_SYMBOL(qpnp_iadc_comp_result);
|
||||||
static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
|
static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
u8 trim2_val = 0, smbb_batt_trm_data = 0;
|
u8 trim2_val = 0;
|
||||||
|
uint smbb_batt_trm_data = 0;
|
||||||
u8 smbb_batt_trm_cnst_rds = 0;
|
u8 smbb_batt_trm_cnst_rds = 0;
|
||||||
|
|
||||||
if (!iadc->rds_trim_default_check) {
|
if (!iadc->rds_trim_default_check) {
|
||||||
|
@ -663,17 +666,17 @@ static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
|
rc = regmap_read(iadc->adc->regmap, iadc->batt_id_trim_cnst_rds,
|
||||||
iadc->batt_id_trim_cnst_rds, &smbb_batt_trm_data, 1);
|
&smbb_batt_trm_data);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
pr_err("batt_id trim_cnst rds reg read failed %d\n", rc);
|
pr_err("batt_id trim_cnst rds reg read failed %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
smbb_batt_trm_cnst_rds = smbb_batt_trm_data &
|
smbb_batt_trm_cnst_rds = (u8)smbb_batt_trm_data &
|
||||||
SMBB_BAT_IF_TRIM_CNST_RDS_MASK;
|
SMBB_BAT_IF_TRIM_CNST_RDS_MASK;
|
||||||
|
|
||||||
pr_debug("n_trim:0x%x smb_trm:0x%x\n", trim2_val, smbb_batt_trm_data);
|
pr_debug("n_trim:0x%x smb_trm:0x%02x\n", trim2_val, smbb_batt_trm_data);
|
||||||
|
|
||||||
if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEA) {
|
if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEA) {
|
||||||
|
|
||||||
|
@ -1084,7 +1087,7 @@ struct qpnp_iadc_chip *qpnp_get_iadc(struct device *dev, const char *name)
|
||||||
return ERR_PTR(-ENODEV);
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
list_for_each_entry(iadc, &qpnp_iadc_device_list, list)
|
list_for_each_entry(iadc, &qpnp_iadc_device_list, list)
|
||||||
if (iadc->adc->spmi->dev.of_node == node)
|
if (iadc->adc->pdev->dev.of_node == node)
|
||||||
return iadc;
|
return iadc;
|
||||||
return ERR_PTR(-EPROBE_DEFER);
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
}
|
}
|
||||||
|
@ -1430,10 +1433,10 @@ static struct sensor_device_attribute qpnp_adc_attr =
|
||||||
SENSOR_ATTR(NULL, S_IRUGO, qpnp_iadc_show, NULL, 0);
|
SENSOR_ATTR(NULL, S_IRUGO, qpnp_iadc_show, NULL, 0);
|
||||||
|
|
||||||
static int32_t qpnp_iadc_init_hwmon(struct qpnp_iadc_chip *iadc,
|
static int32_t qpnp_iadc_init_hwmon(struct qpnp_iadc_chip *iadc,
|
||||||
struct spmi_device *spmi)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
int rc = 0, i = 0, channel;
|
int rc = 0, i = 0, channel;
|
||||||
|
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
|
@ -1444,10 +1447,10 @@ static int32_t qpnp_iadc_init_hwmon(struct qpnp_iadc_chip *iadc,
|
||||||
memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
|
memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
|
||||||
sizeof(qpnp_adc_attr));
|
sizeof(qpnp_adc_attr));
|
||||||
sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
|
sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
|
||||||
rc = device_create_file(&spmi->dev,
|
rc = device_create_file(&pdev->dev,
|
||||||
&iadc->sens_attr[i].dev_attr);
|
&iadc->sens_attr[i].dev_attr);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"device_create_file failed for dev %s\n",
|
"device_create_file failed for dev %s\n",
|
||||||
iadc->adc->adc_channels[i].name);
|
iadc->adc->adc_channels[i].name);
|
||||||
goto hwmon_err_sens;
|
goto hwmon_err_sens;
|
||||||
|
@ -1461,13 +1464,13 @@ hwmon_err_sens:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_iadc_probe(struct spmi_device *spmi)
|
static int qpnp_iadc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_iadc_chip *iadc;
|
struct qpnp_iadc_chip *iadc;
|
||||||
struct qpnp_adc_drv *adc_qpnp;
|
struct qpnp_adc_drv *adc_qpnp;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
int rc, count_adc_channel_list = 0, i = 0;
|
int rc, count_adc_channel_list = 0, i = 0;
|
||||||
|
|
||||||
for_each_child_of_node(node, child)
|
for_each_child_of_node(node, child)
|
||||||
|
@ -1478,37 +1481,45 @@ static int qpnp_iadc_probe(struct spmi_device *spmi)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
iadc = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_iadc_chip) +
|
iadc = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_iadc_chip) +
|
||||||
(sizeof(struct sensor_device_attribute) *
|
(sizeof(struct sensor_device_attribute) *
|
||||||
count_adc_channel_list), GFP_KERNEL);
|
count_adc_channel_list), GFP_KERNEL);
|
||||||
if (!iadc) {
|
if (!iadc) {
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
|
adc_qpnp = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_drv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!adc_qpnp) {
|
if (!adc_qpnp) {
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
iadc->dev = &(spmi->dev);
|
adc_qpnp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!adc_qpnp->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iadc->dev = &(pdev->dev);
|
||||||
iadc->adc = adc_qpnp;
|
iadc->adc = adc_qpnp;
|
||||||
|
|
||||||
rc = qpnp_adc_get_devicetree_data(spmi, iadc->adc);
|
rc = qpnp_adc_get_devicetree_data(pdev, iadc->adc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to read device tree\n");
|
dev_err(&pdev->dev, "failed to read device tree\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
rc = of_property_read_u32(pdev->dev.of_node, "batt-id-trim-cnst-rds",
|
||||||
"batt-id-trim-cnst-rds");
|
&base);
|
||||||
if (!res) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "failed to read batt_id trim register\n");
|
dev_err(&pdev->dev,
|
||||||
return -EINVAL;
|
"Couldn't find batt-id-trim-cnst-rds in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
iadc->batt_id_trim_cnst_rds = res->start;
|
iadc->batt_id_trim_cnst_rds = base;
|
||||||
rc = of_property_read_u32(node, "qcom,use-default-rds-trim",
|
rc = of_property_read_u32(node, "qcom,use-default-rds-trim",
|
||||||
&iadc->rds_trim_default_type);
|
&iadc->rds_trim_default_type);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -1518,7 +1529,7 @@ static int qpnp_iadc_probe(struct spmi_device *spmi)
|
||||||
iadc->rds_trim_default_check = true;
|
iadc->rds_trim_default_check = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
iadc->vadc_dev = qpnp_get_vadc(&spmi->dev, "iadc");
|
iadc->vadc_dev = qpnp_get_vadc(&pdev->dev, "iadc");
|
||||||
if (IS_ERR(iadc->vadc_dev)) {
|
if (IS_ERR(iadc->vadc_dev)) {
|
||||||
rc = PTR_ERR(iadc->vadc_dev);
|
rc = PTR_ERR(iadc->vadc_dev);
|
||||||
if (rc != -EPROBE_DEFER)
|
if (rc != -EPROBE_DEFER)
|
||||||
|
@ -1540,26 +1551,27 @@ static int qpnp_iadc_probe(struct spmi_device *spmi)
|
||||||
iadc->iadc_poll_eoc = of_property_read_bool(node,
|
iadc->iadc_poll_eoc = of_property_read_bool(node,
|
||||||
"qcom,iadc-poll-eoc");
|
"qcom,iadc-poll-eoc");
|
||||||
if (!iadc->iadc_poll_eoc) {
|
if (!iadc->iadc_poll_eoc) {
|
||||||
rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
|
rc = devm_request_irq(&pdev->dev, iadc->adc->adc_irq_eoc,
|
||||||
qpnp_iadc_isr, IRQF_TRIGGER_RISING,
|
qpnp_iadc_isr, IRQF_TRIGGER_RISING,
|
||||||
"qpnp_iadc_interrupt", iadc);
|
"qpnp_iadc_interrupt", iadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to request adc irq\n");
|
dev_err(&pdev->dev, "failed to request adc irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
} else
|
} else {
|
||||||
enable_irq_wake(iadc->adc->adc_irq_eoc);
|
enable_irq_wake(iadc->adc->adc_irq_eoc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_iadc_init_hwmon(iadc, spmi);
|
rc = qpnp_iadc_init_hwmon(iadc, pdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
|
dev_err(&pdev->dev, "failed to initialize qpnp hwmon adc\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->spmi->dev);
|
iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->pdev->dev);
|
||||||
|
|
||||||
rc = qpnp_iadc_version_check(iadc);
|
rc = qpnp_iadc_version_check(iadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "IADC version not supported\n");
|
dev_err(&pdev->dev, "IADC version not supported\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1568,21 +1580,21 @@ static int qpnp_iadc_probe(struct spmi_device *spmi)
|
||||||
INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
|
INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
|
||||||
rc = qpnp_iadc_comp_info(iadc);
|
rc = qpnp_iadc_comp_info(iadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "abstracting IADC comp info failed!\n");
|
dev_err(&pdev->dev, "abstracting IADC comp info failed!\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_iadc_rds_trim_update_check(iadc);
|
rc = qpnp_iadc_rds_trim_update_check(iadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "Rds trim update failed!\n");
|
dev_err(&pdev->dev, "Rds trim update failed!\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, iadc);
|
dev_set_drvdata(&pdev->dev, iadc);
|
||||||
list_add(&iadc->list, &qpnp_iadc_device_list);
|
list_add(&iadc->list, &qpnp_iadc_device_list);
|
||||||
rc = qpnp_iadc_calibrate_for_trim(iadc, true);
|
rc = qpnp_iadc_calibrate_for_trim(iadc, true);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&spmi->dev, "failed to calibrate for USR trim\n");
|
dev_err(&pdev->dev, "failed to calibrate for USR trim\n");
|
||||||
|
|
||||||
if (iadc->iadc_poll_eoc)
|
if (iadc->iadc_poll_eoc)
|
||||||
device_init_wakeup(iadc->dev, 1);
|
device_init_wakeup(iadc->dev, 1);
|
||||||
|
@ -1593,8 +1605,7 @@ static int qpnp_iadc_probe(struct spmi_device *spmi)
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
device_remove_file(&spmi->dev,
|
device_remove_file(&pdev->dev, &iadc->sens_attr[i].dev_attr);
|
||||||
&iadc->sens_attr[i].dev_attr);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
hwmon_device_unregister(iadc->iadc_hwmon);
|
hwmon_device_unregister(iadc->iadc_hwmon);
|
||||||
|
@ -1602,23 +1613,22 @@ fail:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_iadc_remove(struct spmi_device *spmi)
|
static int qpnp_iadc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_iadc_chip *iadc = dev_get_drvdata(&spmi->dev);
|
struct qpnp_iadc_chip *iadc = dev_get_drvdata(&pdev->dev);
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
cancel_delayed_work(&iadc->iadc_work);
|
cancel_delayed_work(&iadc->iadc_work);
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
device_remove_file(&spmi->dev,
|
device_remove_file(&pdev->dev, &iadc->sens_attr[i].dev_attr);
|
||||||
&iadc->sens_attr[i].dev_attr);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
hwmon_device_unregister(iadc->iadc_hwmon);
|
hwmon_device_unregister(iadc->iadc_hwmon);
|
||||||
if (iadc->iadc_poll_eoc)
|
if (iadc->iadc_poll_eoc)
|
||||||
pm_relax(iadc->dev);
|
pm_relax(iadc->dev);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1629,9 +1639,9 @@ static const struct of_device_id qpnp_iadc_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_iadc_driver = {
|
static struct platform_driver qpnp_iadc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-iadc",
|
.name = "qcom,qpnp-iadc",
|
||||||
.of_match_table = qpnp_iadc_match_table,
|
.of_match_table = qpnp_iadc_match_table,
|
||||||
},
|
},
|
||||||
.probe = qpnp_iadc_probe,
|
.probe = qpnp_iadc_probe,
|
||||||
|
@ -1640,13 +1650,13 @@ static struct spmi_driver qpnp_iadc_driver = {
|
||||||
|
|
||||||
static int __init qpnp_iadc_init(void)
|
static int __init qpnp_iadc_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_iadc_driver);
|
return platform_driver_register(&qpnp_iadc_driver);
|
||||||
}
|
}
|
||||||
module_init(qpnp_iadc_init);
|
module_init(qpnp_iadc_init);
|
||||||
|
|
||||||
static void __exit qpnp_iadc_exit(void)
|
static void __exit qpnp_iadc_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_iadc_driver);
|
platform_driver_unregister(&qpnp_iadc_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_iadc_exit);
|
module_exit(qpnp_iadc_exit);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
@ -179,7 +181,6 @@ struct qpnp_vadc_chip {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct qpnp_adc_drv *adc;
|
struct qpnp_adc_drv *adc;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct dentry *dent;
|
|
||||||
struct device *vadc_hwmon;
|
struct device *vadc_hwmon;
|
||||||
bool vadc_init_calib;
|
bool vadc_init_calib;
|
||||||
int max_channels_available;
|
int max_channels_available;
|
||||||
|
@ -190,8 +191,6 @@ struct qpnp_vadc_chip {
|
||||||
bool vadc_recalib_check;
|
bool vadc_recalib_check;
|
||||||
u8 revision_ana_minor;
|
u8 revision_ana_minor;
|
||||||
u8 revision_dig_major;
|
u8 revision_dig_major;
|
||||||
struct workqueue_struct *high_thr_wq;
|
|
||||||
struct workqueue_struct *low_thr_wq;
|
|
||||||
struct work_struct trigger_high_thr_work;
|
struct work_struct trigger_high_thr_work;
|
||||||
struct work_struct trigger_low_thr_work;
|
struct work_struct trigger_low_thr_work;
|
||||||
struct qpnp_vadc_mode_state *state_copy;
|
struct qpnp_vadc_mode_state *state_copy;
|
||||||
|
@ -228,13 +227,15 @@ static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
|
||||||
u8 *data, int len)
|
u8 *data, int len)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vadc->adc->spmi->ctrl, vadc->adc->slave,
|
rc = regmap_bulk_read(vadc->adc->regmap,
|
||||||
(vadc->adc->offset + reg), data, len);
|
(vadc->adc->offset + reg), data, len);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
pr_err("qpnp adc read reg %d failed with %d\n", reg, rc);
|
pr_err("qpnp adc read reg %d failed with %d\n", reg, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
*data = (u8)val;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +245,7 @@ static int32_t qpnp_vadc_write_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(vadc->adc->spmi->ctrl, vadc->adc->slave,
|
rc = regmap_bulk_write(vadc->adc->regmap,
|
||||||
(vadc->adc->offset + reg), buf, len);
|
(vadc->adc->offset + reg), buf, len);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
pr_err("qpnp adc write reg %d failed with %d\n", reg, rc);
|
pr_err("qpnp adc write reg %d failed with %d\n", reg, rc);
|
||||||
|
@ -1530,7 +1531,7 @@ struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev, const char *name)
|
||||||
return ERR_PTR(-ENODEV);
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
list_for_each_entry(vadc, &qpnp_vadc_device_list, list)
|
list_for_each_entry(vadc, &qpnp_vadc_device_list, list)
|
||||||
if (vadc->adc->spmi->dev.of_node == node)
|
if (vadc->adc->pdev->dev.of_node == node)
|
||||||
return vadc;
|
return vadc;
|
||||||
return ERR_PTR(-EPROBE_DEFER);
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
}
|
}
|
||||||
|
@ -2437,10 +2438,10 @@ static struct sensor_device_attribute qpnp_adc_attr =
|
||||||
SENSOR_ATTR(NULL, S_IRUGO, qpnp_adc_show, NULL, 0);
|
SENSOR_ATTR(NULL, S_IRUGO, qpnp_adc_show, NULL, 0);
|
||||||
|
|
||||||
static int32_t qpnp_vadc_init_hwmon(struct qpnp_vadc_chip *vadc,
|
static int32_t qpnp_vadc_init_hwmon(struct qpnp_vadc_chip *vadc,
|
||||||
struct spmi_device *spmi)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
int rc = 0, i = 0, channel;
|
int rc = 0, i = 0, channel;
|
||||||
|
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
|
@ -2451,10 +2452,10 @@ static int32_t qpnp_vadc_init_hwmon(struct qpnp_vadc_chip *vadc,
|
||||||
memcpy(&vadc->sens_attr[i], &qpnp_adc_attr,
|
memcpy(&vadc->sens_attr[i], &qpnp_adc_attr,
|
||||||
sizeof(qpnp_adc_attr));
|
sizeof(qpnp_adc_attr));
|
||||||
sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
|
sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
|
||||||
rc = device_create_file(&spmi->dev,
|
rc = device_create_file(&pdev->dev,
|
||||||
&vadc->sens_attr[i].dev_attr);
|
&vadc->sens_attr[i].dev_attr);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"device_create_file failed for dev %s\n",
|
"device_create_file failed for dev %s\n",
|
||||||
vadc->adc->adc_channels[i].name);
|
vadc->adc->adc_channels[i].name);
|
||||||
goto hwmon_err_sens;
|
goto hwmon_err_sens;
|
||||||
|
@ -2493,10 +2494,10 @@ static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc,
|
static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc,
|
||||||
struct spmi_device *spmi)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
int rc = 0, i = 0;
|
int rc = 0, i = 0;
|
||||||
bool thermal_node = false;
|
bool thermal_node = false;
|
||||||
|
|
||||||
|
@ -2542,12 +2543,12 @@ static const struct of_device_id qpnp_vadc_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int qpnp_vadc_probe(struct spmi_device *spmi)
|
static int qpnp_vadc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_vadc_chip *vadc;
|
struct qpnp_vadc_chip *vadc;
|
||||||
struct qpnp_adc_drv *adc_qpnp;
|
struct qpnp_adc_drv *adc_qpnp;
|
||||||
struct qpnp_vadc_thermal_data *adc_thermal;
|
struct qpnp_vadc_thermal_data *adc_thermal;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
const struct of_device_id *id;
|
const struct of_device_id *id;
|
||||||
int rc, count_adc_channel_list = 0, i = 0;
|
int rc, count_adc_channel_list = 0, i = 0;
|
||||||
|
@ -2567,54 +2568,61 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
vadc = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_vadc_chip) +
|
vadc = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_vadc_chip) +
|
||||||
(sizeof(struct sensor_device_attribute) *
|
(sizeof(struct sensor_device_attribute) *
|
||||||
count_adc_channel_list), GFP_KERNEL);
|
count_adc_channel_list), GFP_KERNEL);
|
||||||
if (!vadc) {
|
if (!vadc) {
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
vadc->dev = &(spmi->dev);
|
vadc->dev = &(pdev->dev);
|
||||||
adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
|
adc_qpnp = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_drv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!adc_qpnp)
|
if (!adc_qpnp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
vadc->state_copy = devm_kzalloc(&spmi->dev,
|
adc_qpnp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!adc_qpnp->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vadc->state_copy = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(struct qpnp_vadc_mode_state), GFP_KERNEL);
|
sizeof(struct qpnp_vadc_mode_state), GFP_KERNEL);
|
||||||
if (!vadc->state_copy)
|
if (!vadc->state_copy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
vadc->adc = adc_qpnp;
|
vadc->adc = adc_qpnp;
|
||||||
adc_thermal = devm_kzalloc(&spmi->dev,
|
adc_thermal = devm_kzalloc(&pdev->dev,
|
||||||
(sizeof(struct qpnp_vadc_thermal_data) *
|
(sizeof(struct qpnp_vadc_thermal_data) *
|
||||||
count_adc_channel_list), GFP_KERNEL);
|
count_adc_channel_list), GFP_KERNEL);
|
||||||
if (!adc_thermal) {
|
if (!adc_thermal) {
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
vadc->vadc_therm_chan = adc_thermal;
|
vadc->vadc_therm_chan = adc_thermal;
|
||||||
|
|
||||||
if (!strcmp(id->compatible, "qcom,qpnp-vadc-hc"))
|
if (!strcmp(id->compatible, "qcom,qpnp-vadc-hc"))
|
||||||
vadc->vadc_hc = true;
|
vadc->vadc_hc = true;
|
||||||
|
|
||||||
rc = qpnp_adc_get_devicetree_data(spmi, vadc->adc);
|
rc = qpnp_adc_get_devicetree_data(pdev, vadc->adc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to read device tree\n");
|
dev_err(&pdev->dev, "failed to read device tree\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
mutex_init(&vadc->adc->adc_lock);
|
mutex_init(&vadc->adc->adc_lock);
|
||||||
|
|
||||||
rc = qpnp_vadc_init_hwmon(vadc, spmi);
|
rc = qpnp_vadc_init_hwmon(vadc, pdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
|
dev_err(&pdev->dev, "failed to initialize qpnp hwmon adc\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
|
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->pdev->dev);
|
||||||
rc = qpnp_vadc_init_thermal(vadc, spmi);
|
rc = qpnp_vadc_init_thermal(vadc, pdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to initialize qpnp thermal adc\n");
|
dev_err(&pdev->dev, "failed to initialize qpnp thermal adc\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
vadc->vadc_init_calib = false;
|
vadc->vadc_init_calib = false;
|
||||||
|
@ -2655,11 +2663,11 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
|
||||||
vadc->vadc_poll_eoc = of_property_read_bool(node,
|
vadc->vadc_poll_eoc = of_property_read_bool(node,
|
||||||
"qcom,vadc-poll-eoc");
|
"qcom,vadc-poll-eoc");
|
||||||
if (!vadc->vadc_poll_eoc) {
|
if (!vadc->vadc_poll_eoc) {
|
||||||
rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq_eoc,
|
rc = devm_request_irq(&pdev->dev, vadc->adc->adc_irq_eoc,
|
||||||
qpnp_vadc_isr, IRQF_TRIGGER_RISING,
|
qpnp_vadc_isr, IRQF_TRIGGER_RISING,
|
||||||
"qpnp_vadc_interrupt", vadc);
|
"qpnp_vadc_interrupt", vadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to request adc irq with error %d\n", rc);
|
"failed to request adc irq with error %d\n", rc);
|
||||||
goto err_setup;
|
goto err_setup;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2671,37 +2679,37 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
|
||||||
vadc->state_copy->vadc_meas_int_enable = of_property_read_bool(node,
|
vadc->state_copy->vadc_meas_int_enable = of_property_read_bool(node,
|
||||||
"qcom,vadc-meas-int-mode");
|
"qcom,vadc-meas-int-mode");
|
||||||
if (vadc->state_copy->vadc_meas_int_enable) {
|
if (vadc->state_copy->vadc_meas_int_enable) {
|
||||||
vadc->adc->adc_high_thr_irq = spmi_get_irq_byname(spmi,
|
vadc->adc->adc_high_thr_irq = platform_get_irq_byname(pdev,
|
||||||
NULL, "high-thr-en-set");
|
"high-thr-en-set");
|
||||||
if (vadc->adc->adc_high_thr_irq < 0) {
|
if (vadc->adc->adc_high_thr_irq < 0) {
|
||||||
pr_err("Invalid irq\n");
|
pr_err("Invalid irq\n");
|
||||||
rc = -ENXIO;
|
rc = -ENXIO;
|
||||||
goto err_setup;
|
goto err_setup;
|
||||||
}
|
}
|
||||||
|
|
||||||
vadc->adc->adc_low_thr_irq = spmi_get_irq_byname(spmi,
|
vadc->adc->adc_low_thr_irq = platform_get_irq_byname(pdev,
|
||||||
NULL, "low-thr-en-set");
|
"low-thr-en-set");
|
||||||
if (vadc->adc->adc_low_thr_irq < 0) {
|
if (vadc->adc->adc_low_thr_irq < 0) {
|
||||||
pr_err("Invalid irq\n");
|
pr_err("Invalid irq\n");
|
||||||
rc = -ENXIO;
|
rc = -ENXIO;
|
||||||
goto err_setup;
|
goto err_setup;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = devm_request_irq(&spmi->dev, vadc->adc->adc_high_thr_irq,
|
rc = devm_request_irq(&pdev->dev, vadc->adc->adc_high_thr_irq,
|
||||||
qpnp_vadc_high_thr_isr,
|
qpnp_vadc_high_thr_isr,
|
||||||
IRQF_TRIGGER_RISING, "qpnp_vadc_high_interrupt", vadc);
|
IRQF_TRIGGER_RISING, "qpnp_vadc_high_interrupt", vadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to request adc irq\n");
|
dev_err(&pdev->dev, "failed to request adc irq\n");
|
||||||
goto err_setup;
|
goto err_setup;
|
||||||
} else {
|
} else {
|
||||||
enable_irq_wake(vadc->adc->adc_high_thr_irq);
|
enable_irq_wake(vadc->adc->adc_high_thr_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = devm_request_irq(&spmi->dev, vadc->adc->adc_low_thr_irq,
|
rc = devm_request_irq(&pdev->dev, vadc->adc->adc_low_thr_irq,
|
||||||
qpnp_vadc_low_thr_isr,
|
qpnp_vadc_low_thr_isr,
|
||||||
IRQF_TRIGGER_RISING, "qpnp_vadc_low_interrupt", vadc);
|
IRQF_TRIGGER_RISING, "qpnp_vadc_low_interrupt", vadc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "failed to request adc irq\n");
|
dev_err(&pdev->dev, "failed to request adc irq\n");
|
||||||
goto err_setup;
|
goto err_setup;
|
||||||
} else {
|
} else {
|
||||||
enable_irq_wake(vadc->adc->adc_low_thr_irq);
|
enable_irq_wake(vadc->adc->adc_low_thr_irq);
|
||||||
|
@ -2712,15 +2720,14 @@ static int qpnp_vadc_probe(struct spmi_device *spmi)
|
||||||
}
|
}
|
||||||
|
|
||||||
vadc->vadc_iadc_sync_lock = false;
|
vadc->vadc_iadc_sync_lock = false;
|
||||||
dev_set_drvdata(&spmi->dev, vadc);
|
dev_set_drvdata(&pdev->dev, vadc);
|
||||||
list_add(&vadc->list, &qpnp_vadc_device_list);
|
list_add(&vadc->list, &qpnp_vadc_device_list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_setup:
|
err_setup:
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
device_remove_file(&spmi->dev,
|
device_remove_file(&pdev->dev, &vadc->sens_attr[i].dev_attr);
|
||||||
&vadc->sens_attr[i].dev_attr);
|
|
||||||
if (vadc->vadc_therm_chan[i].thermal_node)
|
if (vadc->vadc_therm_chan[i].thermal_node)
|
||||||
thermal_zone_device_unregister(
|
thermal_zone_device_unregister(
|
||||||
vadc->vadc_therm_chan[i].tz_dev);
|
vadc->vadc_therm_chan[i].tz_dev);
|
||||||
|
@ -2731,16 +2738,15 @@ err_setup:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_vadc_remove(struct spmi_device *spmi)
|
static int qpnp_vadc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_vadc_chip *vadc = dev_get_drvdata(&spmi->dev);
|
struct qpnp_vadc_chip *vadc = dev_get_drvdata(&pdev->dev);
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for_each_child_of_node(node, child) {
|
for_each_child_of_node(node, child) {
|
||||||
device_remove_file(&spmi->dev,
|
device_remove_file(&pdev->dev, &vadc->sens_attr[i].dev_attr);
|
||||||
&vadc->sens_attr[i].dev_attr);
|
|
||||||
if (vadc->vadc_therm_chan[i].thermal_node)
|
if (vadc->vadc_therm_chan[i].thermal_node)
|
||||||
thermal_zone_device_unregister(
|
thermal_zone_device_unregister(
|
||||||
vadc->vadc_therm_chan[i].tz_dev);
|
vadc->vadc_therm_chan[i].tz_dev);
|
||||||
|
@ -2750,7 +2756,7 @@ static int qpnp_vadc_remove(struct spmi_device *spmi)
|
||||||
list_del(&vadc->list);
|
list_del(&vadc->list);
|
||||||
if (vadc->adc->hkadc_ldo && vadc->adc->hkadc_ldo_ok)
|
if (vadc->adc->hkadc_ldo && vadc->adc->hkadc_ldo_ok)
|
||||||
qpnp_adc_free_voltage_resource(vadc->adc);
|
qpnp_adc_free_voltage_resource(vadc->adc);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2781,10 +2787,10 @@ static const struct dev_pm_ops qpnp_vadc_pm_ops = {
|
||||||
.suspend_noirq = qpnp_vadc_suspend_noirq,
|
.suspend_noirq = qpnp_vadc_suspend_noirq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_vadc_driver = {
|
static struct platform_driver qpnp_vadc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-vadc",
|
.name = "qcom,qpnp-vadc",
|
||||||
.of_match_table = qpnp_vadc_match_table,
|
.of_match_table = qpnp_vadc_match_table,
|
||||||
.pm = &qpnp_vadc_pm_ops,
|
.pm = &qpnp_vadc_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = qpnp_vadc_probe,
|
.probe = qpnp_vadc_probe,
|
||||||
|
@ -2793,13 +2799,13 @@ static struct spmi_driver qpnp_vadc_driver = {
|
||||||
|
|
||||||
static int __init qpnp_vadc_init(void)
|
static int __init qpnp_vadc_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_vadc_driver);
|
return platform_driver_register(&qpnp_vadc_driver);
|
||||||
}
|
}
|
||||||
module_init(qpnp_vadc_init);
|
module_init(qpnp_vadc_init);
|
||||||
|
|
||||||
static void __exit qpnp_vadc_exit(void)
|
static void __exit qpnp_vadc_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_vadc_driver);
|
platform_driver_unregister(&qpnp_vadc_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_vadc_exit);
|
module_exit(qpnp_vadc_exit);
|
||||||
|
|
||||||
|
|
|
@ -581,15 +581,10 @@ config LEDS_POWERNV
|
||||||
depends on LEDS_CLASS
|
depends on LEDS_CLASS
|
||||||
depends on PPC_POWERNV
|
depends on PPC_POWERNV
|
||||||
depends on OF
|
depends on OF
|
||||||
help
|
|
||||||
This option enables support for the system LEDs present on
|
|
||||||
PowerNV platforms. Say 'y' to enable this support in kernel.
|
|
||||||
To compile this driver as a module, choose 'm' here: the module
|
|
||||||
will be called leds-powernv.
|
|
||||||
|
|
||||||
config LEDS_QPNP
|
config LEDS_QPNP
|
||||||
tristate "Support for QPNP LEDs"
|
tristate "Support for QPNP LEDs"
|
||||||
depends on LEDS_CLASS && MSM_SPMI && OF_SPMI
|
depends on LEDS_CLASS && SPMI
|
||||||
help
|
help
|
||||||
This driver supports the leds functionality of Qualcomm PNP PMIC. It
|
This driver supports the leds functionality of Qualcomm PNP PMIC. It
|
||||||
includes RGB Leds, WLED and Flash Led.
|
includes RGB Leds, WLED and Flash Led.
|
||||||
|
@ -599,7 +594,7 @@ config LEDS_QPNP
|
||||||
|
|
||||||
config LEDS_QPNP_FLASH
|
config LEDS_QPNP_FLASH
|
||||||
tristate "Support for QPNP Flash LEDs"
|
tristate "Support for QPNP Flash LEDs"
|
||||||
depends on LEDS_CLASS && MSM_SPMI && OF_SPMI
|
depends on LEDS_CLASS && SPMI
|
||||||
help
|
help
|
||||||
This driver supports the leds functionality of Qualcomm Technologies
|
This driver supports the leds functionality of Qualcomm Technologies
|
||||||
PNP PMIC. It includes Flash Led.
|
PNP PMIC. It includes Flash Led.
|
||||||
|
@ -609,7 +604,36 @@ config LEDS_QPNP_FLASH
|
||||||
|
|
||||||
config LEDS_QPNP_WLED
|
config LEDS_QPNP_WLED
|
||||||
tristate "Support for QPNP WLED"
|
tristate "Support for QPNP WLED"
|
||||||
depends on LEDS_CLASS && MSM_SPMI && OF_SPMI
|
depends on LEDS_CLASS && SPMI
|
||||||
|
help
|
||||||
|
This option enables support for the system LEDs present on
|
||||||
|
PowerNV platforms. Say 'y' to enable this support in kernel.
|
||||||
|
To compile this driver as a module, choose 'm' here: the module
|
||||||
|
will be called leds-powernv.
|
||||||
|
|
||||||
|
config LEDS_QPNP
|
||||||
|
tristate "Support for QPNP LEDs"
|
||||||
|
depends on LEDS_CLASS && SPMI
|
||||||
|
help
|
||||||
|
This driver supports the leds functionality of Qualcomm PNP PMIC. It
|
||||||
|
includes RGB Leds, WLED and Flash Led.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called leds-qpnp.
|
||||||
|
|
||||||
|
config LEDS_QPNP_FLASH
|
||||||
|
tristate "Support for QPNP Flash LEDs"
|
||||||
|
depends on LEDS_CLASS && SPMI
|
||||||
|
help
|
||||||
|
This driver supports the leds functionality of Qualcomm Technologies
|
||||||
|
PNP PMIC. It includes Flash Led.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called leds-qpnp-flash.
|
||||||
|
|
||||||
|
config LEDS_QPNP_WLED
|
||||||
|
tristate "Support for QPNP WLED"
|
||||||
|
depends on LEDS_CLASS && SPMI
|
||||||
help
|
help
|
||||||
This driver supports the WLED (White LED) functionality of
|
This driver supports the WLED (White LED) functionality of
|
||||||
Qualcomm Technologies PNP PMIC. WLED is used for display backlight.
|
Qualcomm Technologies PNP PMIC. WLED is used for display backlight.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,11 +13,14 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -269,54 +272,55 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
|
||||||
*/
|
*/
|
||||||
struct qpnp_wled {
|
struct qpnp_wled {
|
||||||
struct led_classdev cdev;
|
struct led_classdev cdev;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
struct work_struct work;
|
struct regmap *regmap;
|
||||||
struct mutex lock;
|
struct work_struct work;
|
||||||
enum qpnp_wled_fdbk_op fdbk_op;
|
struct mutex lock;
|
||||||
enum qpnp_wled_dim_mode dim_mode;
|
enum qpnp_wled_fdbk_op fdbk_op;
|
||||||
int ovp_irq;
|
enum qpnp_wled_dim_mode dim_mode;
|
||||||
int sc_irq;
|
int ovp_irq;
|
||||||
u32 sc_cnt;
|
int sc_irq;
|
||||||
u32 avdd_trim_steps_from_center;
|
u32 sc_cnt;
|
||||||
u16 ctrl_base;
|
u32 avdd_trim_steps_from_center;
|
||||||
u16 sink_base;
|
u16 ctrl_base;
|
||||||
u16 mod_freq_khz;
|
u16 sink_base;
|
||||||
u16 hyb_thres;
|
u16 mod_freq_khz;
|
||||||
u16 sync_dly_us;
|
u16 hyb_thres;
|
||||||
u16 vref_mv;
|
u16 sync_dly_us;
|
||||||
u16 vref_psm_mv;
|
u16 vref_mv;
|
||||||
u16 loop_comp_res_kohm;
|
u16 vref_psm_mv;
|
||||||
u16 loop_ea_gm;
|
u16 loop_comp_res_kohm;
|
||||||
u16 sc_deb_cycles;
|
u16 loop_ea_gm;
|
||||||
u16 switch_freq_khz;
|
u16 sc_deb_cycles;
|
||||||
u16 ovp_mv;
|
u16 switch_freq_khz;
|
||||||
u16 ilim_ma;
|
u16 ovp_mv;
|
||||||
u16 boost_duty_ns;
|
u16 ilim_ma;
|
||||||
u16 fs_curr_ua;
|
u16 boost_duty_ns;
|
||||||
u16 ramp_ms;
|
u16 fs_curr_ua;
|
||||||
u16 ramp_step;
|
u16 ramp_ms;
|
||||||
u16 cons_sync_write_delay_us;
|
u16 ramp_step;
|
||||||
u8 strings[QPNP_WLED_MAX_STRINGS];
|
u16 cons_sync_write_delay_us;
|
||||||
u8 num_strings;
|
u8 strings[QPNP_WLED_MAX_STRINGS];
|
||||||
bool en_9b_dim_res;
|
u8 num_strings;
|
||||||
bool en_phase_stag;
|
bool en_9b_dim_res;
|
||||||
bool en_cabc;
|
bool en_phase_stag;
|
||||||
bool disp_type_amoled;
|
bool en_cabc;
|
||||||
bool en_ext_pfet_sc_pro;
|
bool disp_type_amoled;
|
||||||
bool prev_state;
|
bool en_ext_pfet_sc_pro;
|
||||||
|
bool prev_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* helper to read a pmic register */
|
/* helper to read a pmic register */
|
||||||
static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
|
static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(wled->spmi->ctrl, wled->spmi->sid,
|
rc = regmap_read(wled->regmap, addr, &val);
|
||||||
addr, data, 1);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
dev_err(&wled->spmi->dev,
|
dev_err(&wled->pdev->dev,
|
||||||
"Error reading address: %x(%d)\n", addr, rc);
|
"Error reading address: %x(%d)\n", addr, rc);
|
||||||
|
*data = (u8)val;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,13 +329,12 @@ static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(wled->spmi->ctrl, wled->spmi->sid,
|
rc = regmap_write(wled->regmap, addr, *data);
|
||||||
addr, data, 1);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
dev_err(&wled->spmi->dev,
|
dev_err(&wled->pdev->dev,
|
||||||
"Error writing address: %x(%d)\n", addr, rc);
|
"Error writing address: %x(%d)\n", addr, rc);
|
||||||
|
|
||||||
dev_dbg(&wled->spmi->dev, "write: WLED_0x%x = 0x%x\n", addr, *data);
|
dev_dbg(&wled->pdev->dev, "write: WLED_0x%x = 0x%x\n", addr, *data);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -400,7 +403,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
|
||||||
|
|
||||||
rc = qpnp_wled_sync_reg_toggle(wled);
|
rc = qpnp_wled_sync_reg_toggle(wled);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&wled->spmi->dev, "Failed to toggle sync reg %d\n", rc);
|
dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +461,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev,
|
||||||
if (!wled->cdev.brightness) {
|
if (!wled->cdev.brightness) {
|
||||||
rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
|
rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&wled->spmi->dev, "wled enable failed\n");
|
dev_err(&wled->pdev->dev, "wled enable failed\n");
|
||||||
goto unlock_mutex;
|
goto unlock_mutex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,7 +470,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev,
|
||||||
for (i = 0; i <= wled->cdev.max_brightness;) {
|
for (i = 0; i <= wled->cdev.max_brightness;) {
|
||||||
rc = qpnp_wled_set_level(wled, i);
|
rc = qpnp_wled_set_level(wled, i);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&wled->spmi->dev, "wled set level failed\n");
|
dev_err(&wled->pdev->dev, "wled set level failed\n");
|
||||||
goto restore_brightness;
|
goto restore_brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +492,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev,
|
||||||
for (i = wled->cdev.max_brightness; i >= 0;) {
|
for (i = wled->cdev.max_brightness; i >= 0;) {
|
||||||
rc = qpnp_wled_set_level(wled, i);
|
rc = qpnp_wled_set_level(wled, i);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&wled->spmi->dev, "wled set level failed\n");
|
dev_err(&wled->pdev->dev, "wled set level failed\n");
|
||||||
goto restore_brightness;
|
goto restore_brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +510,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev,
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&wled->spmi->dev, "wled ramp complete\n");
|
dev_info(&wled->pdev->dev, "wled ramp complete\n");
|
||||||
|
|
||||||
restore_brightness:
|
restore_brightness:
|
||||||
/* restore the old brightness */
|
/* restore the old brightness */
|
||||||
|
@ -515,7 +518,7 @@ restore_brightness:
|
||||||
if (!wled->cdev.brightness) {
|
if (!wled->cdev.brightness) {
|
||||||
rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
|
rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&wled->spmi->dev, "wled enable failed\n");
|
dev_err(&wled->pdev->dev, "wled enable failed\n");
|
||||||
}
|
}
|
||||||
unlock_mutex:
|
unlock_mutex:
|
||||||
mutex_unlock(&wled->lock);
|
mutex_unlock(&wled->lock);
|
||||||
|
@ -728,7 +731,7 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
|
||||||
|
|
||||||
rc = qpnp_wled_sync_reg_toggle(wled);
|
rc = qpnp_wled_sync_reg_toggle(wled);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&wled->spmi->dev, "Failed to toggle sync reg %d\n", rc);
|
dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,7 +775,7 @@ static void qpnp_wled_work(struct work_struct *work)
|
||||||
if (level) {
|
if (level) {
|
||||||
rc = qpnp_wled_set_level(wled, level);
|
rc = qpnp_wled_set_level(wled, level);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&wled->spmi->dev, "wled set level failed\n");
|
dev_err(&wled->pdev->dev, "wled set level failed\n");
|
||||||
goto unlock_mutex;
|
goto unlock_mutex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -781,7 +784,7 @@ static void qpnp_wled_work(struct work_struct *work)
|
||||||
rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
|
rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&wled->spmi->dev, "wled %sable failed\n",
|
dev_err(&wled->pdev->dev, "wled %sable failed\n",
|
||||||
level ? "en" : "dis");
|
level ? "en" : "dis");
|
||||||
goto unlock_mutex;
|
goto unlock_mutex;
|
||||||
}
|
}
|
||||||
|
@ -950,7 +953,7 @@ static irqreturn_t qpnp_wled_ovp_irq(int irq, void *_wled)
|
||||||
{
|
{
|
||||||
struct qpnp_wled *wled = _wled;
|
struct qpnp_wled *wled = _wled;
|
||||||
|
|
||||||
dev_dbg(&wled->spmi->dev, "ovp detected\n");
|
dev_dbg(&wled->pdev->dev, "ovp detected\n");
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -960,7 +963,7 @@ static irqreturn_t qpnp_wled_sc_irq(int irq, void *_wled)
|
||||||
{
|
{
|
||||||
struct qpnp_wled *wled = _wled;
|
struct qpnp_wled *wled = _wled;
|
||||||
|
|
||||||
dev_err(&wled->spmi->dev,
|
dev_err(&wled->pdev->dev,
|
||||||
"Short circuit detected %d times\n", ++wled->sc_cnt);
|
"Short circuit detected %d times\n", ++wled->sc_cnt);
|
||||||
|
|
||||||
qpnp_wled_module_en(wled, wled->ctrl_base, false);
|
qpnp_wled_module_en(wled, wled->ctrl_base, false);
|
||||||
|
@ -1205,7 +1208,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
||||||
|
|
||||||
for (i = 0; i < wled->num_strings; i++) {
|
for (i = 0; i < wled->num_strings; i++) {
|
||||||
if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
|
if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
|
||||||
dev_err(&wled->spmi->dev, "Invalid string number\n");
|
dev_err(&wled->pdev->dev, "Invalid string number\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1294,18 +1297,19 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
||||||
|
|
||||||
rc = qpnp_wled_sync_reg_toggle(wled);
|
rc = qpnp_wled_sync_reg_toggle(wled);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&wled->spmi->dev, "Failed to toggle sync reg %d\n", rc);
|
dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setup ovp and sc irqs */
|
/* setup ovp and sc irqs */
|
||||||
if (wled->ovp_irq >= 0) {
|
if (wled->ovp_irq >= 0) {
|
||||||
rc = devm_request_threaded_irq(&wled->spmi->dev, wled->ovp_irq,
|
rc = devm_request_threaded_irq(&wled->pdev->dev,
|
||||||
|
wled->ovp_irq,
|
||||||
NULL, qpnp_wled_ovp_irq,
|
NULL, qpnp_wled_ovp_irq,
|
||||||
QPNP_IRQ_FLAGS,
|
QPNP_IRQ_FLAGS,
|
||||||
"qpnp_wled_ovp_irq", wled);
|
"qpnp_wled_ovp_irq", wled);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&wled->spmi->dev,
|
dev_err(&wled->pdev->dev,
|
||||||
"Unable to request ovp(%d) IRQ(err:%d)\n",
|
"Unable to request ovp(%d) IRQ(err:%d)\n",
|
||||||
wled->ovp_irq, rc);
|
wled->ovp_irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1314,12 +1318,12 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
||||||
|
|
||||||
if (wled->sc_irq >= 0) {
|
if (wled->sc_irq >= 0) {
|
||||||
wled->sc_cnt = 0;
|
wled->sc_cnt = 0;
|
||||||
rc = devm_request_threaded_irq(&wled->spmi->dev, wled->sc_irq,
|
rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
|
||||||
NULL, qpnp_wled_sc_irq,
|
NULL, qpnp_wled_sc_irq,
|
||||||
QPNP_IRQ_FLAGS,
|
QPNP_IRQ_FLAGS,
|
||||||
"qpnp_wled_sc_irq", wled);
|
"qpnp_wled_sc_irq", wled);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&wled->spmi->dev,
|
dev_err(&wled->pdev->dev,
|
||||||
"Unable to request sc(%d) IRQ(err:%d)\n",
|
"Unable to request sc(%d) IRQ(err:%d)\n",
|
||||||
wled->sc_irq, rc);
|
wled->sc_irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1369,7 +1373,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
||||||
/* parse wled dtsi parameters */
|
/* parse wled dtsi parameters */
|
||||||
static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = wled->spmi;
|
struct platform_device *pdev = wled->pdev;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
const char *temp_str;
|
const char *temp_str;
|
||||||
u32 temp_val;
|
u32 temp_val;
|
||||||
|
@ -1377,78 +1381,78 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||||
u8 *strings;
|
u8 *strings;
|
||||||
|
|
||||||
wled->cdev.name = "wled";
|
wled->cdev.name = "wled";
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"linux,name", &wled->cdev.name);
|
"linux,name", &wled->cdev.name);
|
||||||
if (rc && (rc != -EINVAL)) {
|
if (rc && (rc != -EINVAL)) {
|
||||||
dev_err(&spmi->dev, "Unable to read led name\n");
|
dev_err(&pdev->dev, "Unable to read led name\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
|
wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
|
||||||
rc = of_property_read_string(spmi->dev.of_node, "linux,default-trigger",
|
rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
|
||||||
&wled->cdev.default_trigger);
|
&wled->cdev.default_trigger);
|
||||||
if (rc && (rc != -EINVAL)) {
|
if (rc && (rc != -EINVAL)) {
|
||||||
dev_err(&spmi->dev, "Unable to read led trigger\n");
|
dev_err(&pdev->dev, "Unable to read led trigger\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->disp_type_amoled = of_property_read_bool(spmi->dev.of_node,
|
wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,disp-type-amoled");
|
"qcom,disp-type-amoled");
|
||||||
if (wled->disp_type_amoled) {
|
if (wled->disp_type_amoled) {
|
||||||
wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
|
wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,vref-psm-mv", &temp_val);
|
"qcom,vref-psm-mv", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->vref_psm_mv = temp_val;
|
wled->vref_psm_mv = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read vref-psm\n");
|
dev_err(&pdev->dev, "Unable to read vref-psm\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->loop_comp_res_kohm =
|
wled->loop_comp_res_kohm =
|
||||||
QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
|
QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,loop-comp-res-kohm", &temp_val);
|
"qcom,loop-comp-res-kohm", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->loop_comp_res_kohm = temp_val;
|
wled->loop_comp_res_kohm = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read loop-comp-res-kohm\n");
|
dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED;
|
wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,loop-ea-gm", &temp_val);
|
"qcom,loop-ea-gm", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->loop_ea_gm = temp_val;
|
wled->loop_ea_gm = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read loop-ea-gm\n");
|
dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT_AMOLED;
|
wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT_AMOLED;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,sc-deb-cycles", &temp_val);
|
"qcom,sc-deb-cycles", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->sc_deb_cycles = temp_val;
|
wled->sc_deb_cycles = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read sc debounce cycles\n");
|
dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->avdd_trim_steps_from_center = 0;
|
wled->avdd_trim_steps_from_center = 0;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,avdd-trim-steps-from-center", &temp_val);
|
"qcom,avdd-trim-steps-from-center", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->avdd_trim_steps_from_center = temp_val;
|
wled->avdd_trim_steps_from_center = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read avdd trim steps from center value\n");
|
dev_err(&pdev->dev, "Unable to read avdd trim steps from center value\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
|
wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,fdbk-output", &temp_str);
|
"qcom,fdbk-output", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "wled1") == 0)
|
if (strcmp(temp_str, "wled1") == 0)
|
||||||
|
@ -1462,72 +1466,72 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||||
else
|
else
|
||||||
wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
|
wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read feedback output\n");
|
dev_err(&pdev->dev, "Unable to read feedback output\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
|
wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,vref-mv", &temp_val);
|
"qcom,vref-mv", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->vref_mv = temp_val;
|
wled->vref_mv = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read vref\n");
|
dev_err(&pdev->dev, "Unable to read vref\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
|
wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,switch-freq-khz", &temp_val);
|
"qcom,switch-freq-khz", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->switch_freq_khz = temp_val;
|
wled->switch_freq_khz = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read switch freq\n");
|
dev_err(&pdev->dev, "Unable to read switch freq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
|
wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,ovp-mv", &temp_val);
|
"qcom,ovp-mv", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->ovp_mv = temp_val;
|
wled->ovp_mv = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read vref\n");
|
dev_err(&pdev->dev, "Unable to read vref\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
|
wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,ilim-ma", &temp_val);
|
"qcom,ilim-ma", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->ilim_ma = temp_val;
|
wled->ilim_ma = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read ilim\n");
|
dev_err(&pdev->dev, "Unable to read ilim\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
|
wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,boost-duty-ns", &temp_val);
|
"qcom,boost-duty-ns", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->boost_duty_ns = temp_val;
|
wled->boost_duty_ns = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read boost duty\n");
|
dev_err(&pdev->dev, "Unable to read boost duty\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
|
wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,mod-freq-khz", &temp_val);
|
"qcom,mod-freq-khz", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->mod_freq_khz = temp_val;
|
wled->mod_freq_khz = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read modulation freq\n");
|
dev_err(&pdev->dev, "Unable to read modulation freq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->dim_mode = QPNP_WLED_DIM_HYBRID;
|
wled->dim_mode = QPNP_WLED_DIM_HYBRID;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,dim-mode", &temp_str);
|
"qcom,dim-mode", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "analog") == 0)
|
if (strcmp(temp_str, "analog") == 0)
|
||||||
|
@ -1537,59 +1541,59 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||||
else
|
else
|
||||||
wled->dim_mode = QPNP_WLED_DIM_HYBRID;
|
wled->dim_mode = QPNP_WLED_DIM_HYBRID;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read dim mode\n");
|
dev_err(&pdev->dev, "Unable to read dim mode\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
|
if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
|
||||||
wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
|
wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,hyb-thres", &temp_val);
|
"qcom,hyb-thres", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->hyb_thres = temp_val;
|
wled->hyb_thres = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read hyb threshold\n");
|
dev_err(&pdev->dev, "Unable to read hyb threshold\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
|
wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,sync-dly-us", &temp_val);
|
"qcom,sync-dly-us", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->sync_dly_us = temp_val;
|
wled->sync_dly_us = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read sync delay\n");
|
dev_err(&pdev->dev, "Unable to read sync delay\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
|
wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,fs-curr-ua", &temp_val);
|
"qcom,fs-curr-ua", &temp_val);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
wled->fs_curr_ua = temp_val;
|
wled->fs_curr_ua = temp_val;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read full scale current\n");
|
dev_err(&pdev->dev, "Unable to read full scale current\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->cons_sync_write_delay_us = 0;
|
wled->cons_sync_write_delay_us = 0;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,cons-sync-write-delay-us", &temp_val);
|
"qcom,cons-sync-write-delay-us", &temp_val);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
wled->cons_sync_write_delay_us = temp_val;
|
wled->cons_sync_write_delay_us = temp_val;
|
||||||
|
|
||||||
wled->en_9b_dim_res = of_property_read_bool(spmi->dev.of_node,
|
wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,en-9b-dim-res");
|
"qcom,en-9b-dim-res");
|
||||||
wled->en_phase_stag = of_property_read_bool(spmi->dev.of_node,
|
wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,en-phase-stag");
|
"qcom,en-phase-stag");
|
||||||
wled->en_cabc = of_property_read_bool(spmi->dev.of_node,
|
wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,en-cabc");
|
"qcom,en-cabc");
|
||||||
|
|
||||||
prop = of_find_property(spmi->dev.of_node,
|
prop = of_find_property(pdev->dev.of_node,
|
||||||
"qcom,led-strings-list", &temp_val);
|
"qcom,led-strings-list", &temp_val);
|
||||||
if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
|
if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
|
||||||
dev_err(&spmi->dev, "Invalid strings info, use default");
|
dev_err(&pdev->dev, "Invalid strings info, use default");
|
||||||
wled->num_strings = QPNP_WLED_MAX_STRINGS;
|
wled->num_strings = QPNP_WLED_MAX_STRINGS;
|
||||||
for (i = 0; i < wled->num_strings; i++)
|
for (i = 0; i < wled->num_strings; i++)
|
||||||
wled->strings[i] = i;
|
wled->strings[i] = i;
|
||||||
|
@ -1600,61 +1604,64 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||||
wled->strings[i] = strings[i];
|
wled->strings[i] = strings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
wled->ovp_irq = spmi_get_irq_byname(spmi, NULL, "ovp-irq");
|
wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
|
||||||
if (wled->ovp_irq < 0)
|
if (wled->ovp_irq < 0)
|
||||||
dev_dbg(&spmi->dev, "ovp irq is not used\n");
|
dev_dbg(&pdev->dev, "ovp irq is not used\n");
|
||||||
|
|
||||||
wled->sc_irq = spmi_get_irq_byname(spmi, NULL, "sc-irq");
|
wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
|
||||||
if (wled->sc_irq < 0)
|
if (wled->sc_irq < 0)
|
||||||
dev_dbg(&spmi->dev, "sc irq is not used\n");
|
dev_dbg(&pdev->dev, "sc irq is not used\n");
|
||||||
|
|
||||||
wled->en_ext_pfet_sc_pro = of_property_read_bool(spmi->dev.of_node,
|
wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,en-ext-pfet-sc-pro");
|
"qcom,en-ext-pfet-sc-pro");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_wled_probe(struct spmi_device *spmi)
|
static int qpnp_wled_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_wled *wled;
|
struct qpnp_wled *wled;
|
||||||
struct resource *wled_resource;
|
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
const __be32 *prop;
|
||||||
|
|
||||||
wled = devm_kzalloc(&spmi->dev, sizeof(*wled), GFP_KERNEL);
|
wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
|
||||||
if (!wled)
|
if (!wled)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!wled->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
wled->spmi = spmi;
|
wled->pdev = pdev;
|
||||||
|
|
||||||
wled_resource = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
|
||||||
QPNP_WLED_SINK_BASE);
|
0, 0);
|
||||||
if (!wled_resource) {
|
if (!prop) {
|
||||||
dev_err(&spmi->dev, "Unable to get wled sink base address\n");
|
dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
|
||||||
return -EINVAL;
|
return rc;
|
||||||
}
|
}
|
||||||
|
wled->sink_base = be32_to_cpu(*prop);
|
||||||
|
|
||||||
wled->sink_base = wled_resource->start;
|
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
|
||||||
|
0, 0);
|
||||||
wled_resource = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
if (!prop) {
|
||||||
QPNP_WLED_CTRL_BASE);
|
dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
|
||||||
if (!wled_resource) {
|
return rc;
|
||||||
dev_err(&spmi->dev, "Unable to get wled ctrl base address\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
wled->ctrl_base = be32_to_cpu(*prop);
|
||||||
|
|
||||||
wled->ctrl_base = wled_resource->start;
|
dev_set_drvdata(&pdev->dev, wled);
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, wled);
|
|
||||||
|
|
||||||
rc = qpnp_wled_parse_dt(wled);
|
rc = qpnp_wled_parse_dt(wled);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "DT parsing failed\n");
|
dev_err(&pdev->dev, "DT parsing failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_wled_config(wled);
|
rc = qpnp_wled_config(wled);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "wled config failed\n");
|
dev_err(&pdev->dev, "wled config failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1668,9 +1675,9 @@ static int qpnp_wled_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
|
wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
|
||||||
|
|
||||||
rc = led_classdev_register(&spmi->dev, &wled->cdev);
|
rc = led_classdev_register(&pdev->dev, &wled->cdev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "wled registration failed(%d)\n", rc);
|
dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
|
||||||
goto wled_register_fail;
|
goto wled_register_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,7 +1685,7 @@ static int qpnp_wled_probe(struct spmi_device *spmi)
|
||||||
rc = sysfs_create_file(&wled->cdev.dev->kobj,
|
rc = sysfs_create_file(&wled->cdev.dev->kobj,
|
||||||
&qpnp_wled_attrs[i].attr);
|
&qpnp_wled_attrs[i].attr);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "sysfs creation failed\n");
|
dev_err(&pdev->dev, "sysfs creation failed\n");
|
||||||
goto sysfs_fail;
|
goto sysfs_fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1696,9 +1703,9 @@ wled_register_fail:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_wled_remove(struct spmi_device *spmi)
|
static int qpnp_wled_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_wled *wled = dev_get_drvdata(&spmi->dev);
|
struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
|
for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
|
||||||
|
@ -1717,10 +1724,10 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_wled_driver = {
|
static struct platform_driver qpnp_wled_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-wled",
|
.name = "qcom,qpnp-wled",
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
},
|
},
|
||||||
.probe = qpnp_wled_probe,
|
.probe = qpnp_wled_probe,
|
||||||
.remove = qpnp_wled_remove,
|
.remove = qpnp_wled_remove,
|
||||||
|
@ -1728,13 +1735,13 @@ static struct spmi_driver qpnp_wled_driver = {
|
||||||
|
|
||||||
static int __init qpnp_wled_init(void)
|
static int __init qpnp_wled_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_wled_driver);
|
return platform_driver_register(&qpnp_wled_driver);
|
||||||
}
|
}
|
||||||
module_init(qpnp_wled_init);
|
module_init(qpnp_wled_init);
|
||||||
|
|
||||||
static void __exit qpnp_wled_exit(void)
|
static void __exit qpnp_wled_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_wled_driver);
|
platform_driver_unregister(&qpnp_wled_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_wled_exit);
|
module_exit(qpnp_wled_exit);
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -708,7 +708,7 @@ config MFD_QCOM_RPM
|
||||||
|
|
||||||
config MFD_SPMI_PMIC
|
config MFD_SPMI_PMIC
|
||||||
tristate "Qualcomm SPMI PMICs"
|
tristate "Qualcomm SPMI PMICs"
|
||||||
depends on ARCH_QCOM || COMPILE_TEST
|
depends on ARCH_QCOM || COMPILE_TEST || ARCH_MSM
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on SPMI
|
depends on SPMI
|
||||||
select REGMAP_SPMI
|
select REGMAP_SPMI
|
||||||
|
|
|
@ -3,7 +3,7 @@ menu "Qualcomm MSM specific device drivers"
|
||||||
|
|
||||||
config QPNP_POWER_ON
|
config QPNP_POWER_ON
|
||||||
tristate "QPNP PMIC POWER-ON Driver"
|
tristate "QPNP PMIC POWER-ON Driver"
|
||||||
depends on OF_SPMI && (SPMI || MSM_SPMI) && MSM_QPNP_INT && INPUT
|
depends on SPMI && INPUT
|
||||||
help
|
help
|
||||||
This driver supports the power-on functionality on Qualcomm
|
This driver supports the power-on functionality on Qualcomm
|
||||||
PNP PMIC. It currently supports reporting the change in status of
|
PNP PMIC. It currently supports reporting the change in status of
|
||||||
|
@ -11,7 +11,7 @@ config QPNP_POWER_ON
|
||||||
|
|
||||||
config QPNP_REVID
|
config QPNP_REVID
|
||||||
tristate "QPNP Revision ID Peripheral"
|
tristate "QPNP Revision ID Peripheral"
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
Say 'y' here to include support for the Qualcomm QPNP REVID
|
Say 'y' here to include support for the Qualcomm QPNP REVID
|
||||||
peripheral. REVID prints out the PMIC type and revision numbers
|
peripheral. REVID prints out the PMIC type and revision numbers
|
||||||
|
@ -20,7 +20,7 @@ config QPNP_REVID
|
||||||
|
|
||||||
config QPNP_COINCELL
|
config QPNP_COINCELL
|
||||||
tristate "Qualcomm QPNP coincell charger support"
|
tristate "Qualcomm QPNP coincell charger support"
|
||||||
depends on (SPMI || MSM_SPMI) && OF_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
This driver supports the QPNP coincell peripheral found inside of
|
This driver supports the QPNP coincell peripheral found inside of
|
||||||
Qualcomm QPNP PMIC devices. The coincell charger provides a means to
|
Qualcomm QPNP PMIC devices. The coincell charger provides a means to
|
||||||
|
@ -30,7 +30,6 @@ config QPNP_COINCELL
|
||||||
|
|
||||||
config QPNP_HAPTIC
|
config QPNP_HAPTIC
|
||||||
tristate "Haptic support for QPNP PMIC"
|
tristate "Haptic support for QPNP PMIC"
|
||||||
depends on OF_SPMI
|
|
||||||
help
|
help
|
||||||
This option enables device driver support for the Haptic
|
This option enables device driver support for the Haptic
|
||||||
on the Qualcomm Technologies' QPNP PMICs. It uses the android
|
on the Qualcomm Technologies' QPNP PMICs. It uses the android
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -13,9 +13,11 @@
|
||||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -23,7 +25,8 @@
|
||||||
#define QPNP_COINCELL_DRIVER_NAME "qcom,qpnp-coincell"
|
#define QPNP_COINCELL_DRIVER_NAME "qcom,qpnp-coincell"
|
||||||
|
|
||||||
struct qpnp_coincell {
|
struct qpnp_coincell {
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
u16 base_addr;
|
u16 base_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,10 +59,11 @@ static int qpnp_coincell_set_resistance(struct qpnp_coincell *chip, int rset)
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = i;
|
reg = i;
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
chip->base_addr + QPNP_COINCELL_REG_RSET, ®, 1);
|
chip->base_addr + QPNP_COINCELL_REG_RSET, reg);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: could not write to RSET register, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: could not write to RSET register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -80,10 +84,11 @@ static int qpnp_coincell_set_voltage(struct qpnp_coincell *chip, int vset)
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = i;
|
reg = i;
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
chip->base_addr + QPNP_COINCELL_REG_VSET, ®, 1);
|
chip->base_addr + QPNP_COINCELL_REG_VSET, reg);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: could not write to VSET register, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: could not write to VSET register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -95,10 +100,11 @@ static int qpnp_coincell_set_charge(struct qpnp_coincell *chip, bool enabled)
|
||||||
u8 reg;
|
u8 reg;
|
||||||
|
|
||||||
reg = enabled ? QPNP_COINCELL_ENABLE : QPNP_COINCELL_DISABLE;
|
reg = enabled ? QPNP_COINCELL_ENABLE : QPNP_COINCELL_DISABLE;
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
chip->base_addr + QPNP_COINCELL_REG_ENABLE, ®, 1);
|
chip->base_addr + QPNP_COINCELL_REG_ENABLE, reg);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: could not write to ENABLE register, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: could not write to ENABLE register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -110,17 +116,20 @@ static void qpnp_coincell_charger_show_state(struct qpnp_coincell *chip)
|
||||||
bool enabled;
|
bool enabled;
|
||||||
u8 reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET + 1];
|
u8 reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET + 1];
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_bulk_read(chip->regmap,
|
||||||
chip->base_addr + QPNP_COINCELL_REG_RSET, reg, ARRAY_SIZE(reg));
|
chip->base_addr + QPNP_COINCELL_REG_RSET, reg,
|
||||||
|
ARRAY_SIZE(reg));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: could not read RSET register, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: could not read RSET register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp = reg[QPNP_COINCELL_REG_RSET - QPNP_COINCELL_REG_RSET];
|
temp = reg[QPNP_COINCELL_REG_RSET - QPNP_COINCELL_REG_RSET];
|
||||||
if (temp >= ARRAY_SIZE(qpnp_rset_map)) {
|
if (temp >= ARRAY_SIZE(qpnp_rset_map)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "unknown RSET=0x%02X register value\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"unknown RSET=0x%02X register value\n",
|
||||||
temp);
|
temp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +137,8 @@ static void qpnp_coincell_charger_show_state(struct qpnp_coincell *chip)
|
||||||
|
|
||||||
temp = reg[QPNP_COINCELL_REG_VSET - QPNP_COINCELL_REG_RSET];
|
temp = reg[QPNP_COINCELL_REG_VSET - QPNP_COINCELL_REG_RSET];
|
||||||
if (temp >= ARRAY_SIZE(qpnp_vset_map)) {
|
if (temp >= ARRAY_SIZE(qpnp_vset_map)) {
|
||||||
dev_err(&chip->spmi_dev->dev, "unknown VSET=0x%02X register value\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"unknown VSET=0x%02X register value\n",
|
||||||
temp);
|
temp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -146,16 +156,19 @@ static int qpnp_coincell_check_type(struct qpnp_coincell *chip)
|
||||||
int rc;
|
int rc;
|
||||||
u8 type[2];
|
u8 type[2];
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_bulk_read(chip->regmap,
|
||||||
chip->base_addr + QPNP_COINCELL_REG_TYPE, type, 2);
|
chip->base_addr + QPNP_COINCELL_REG_TYPE, type,
|
||||||
|
2);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: could not read type register, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: could not read type register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type[0] != QPNP_COINCELL_TYPE || type[1] != QPNP_COINCELL_SUBTYPE) {
|
if (type[0] != QPNP_COINCELL_TYPE || type[1] != QPNP_COINCELL_SUBTYPE) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: invalid type=0x%02X or subtype=0x%02X register value\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: invalid type=0x%02X or subtype=0x%02X register value\n",
|
||||||
__func__, type[0], type[1]);
|
__func__, type[0], type[1]);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -163,34 +176,38 @@ static int qpnp_coincell_check_type(struct qpnp_coincell *chip)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_coincell_probe(struct spmi_device *spmi)
|
static int qpnp_coincell_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct qpnp_coincell *chip;
|
struct qpnp_coincell *chip;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
dev_err(&spmi->dev, "%s: device node missing\n", __func__);
|
dev_err(&pdev->dev, "%s: device node missing\n", __func__);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip = devm_kzalloc(&spmi->dev, sizeof(*chip), GFP_KERNEL);
|
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
if (!chip) {
|
if (!chip)
|
||||||
dev_err(&spmi->dev, "%s: cannot allocate qpnp_coincell\n",
|
|
||||||
__func__);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
chip->spmi_dev = spmi;
|
|
||||||
|
|
||||||
res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
if (!res) {
|
if (!chip->regmap) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing base address\n",
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
__func__);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
chip->base_addr = res->start;
|
chip->pdev = pdev;
|
||||||
|
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
chip->base_addr = base;
|
||||||
|
|
||||||
rc = qpnp_coincell_check_type(chip);
|
rc = qpnp_coincell_check_type(chip);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -222,7 +239,7 @@ static int qpnp_coincell_probe(struct spmi_device *spmi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_coincell_remove(struct spmi_device *spmi)
|
static int qpnp_coincell_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -232,13 +249,13 @@ static struct of_device_id qpnp_coincell_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_coincell_id[] = {
|
static const struct platform_device_id qpnp_coincell_id[] = {
|
||||||
{ QPNP_COINCELL_DRIVER_NAME, 0 },
|
{ QPNP_COINCELL_DRIVER_NAME, 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, qpnp_coincell_id);
|
MODULE_DEVICE_TABLE(spmi, qpnp_coincell_id);
|
||||||
|
|
||||||
static struct spmi_driver qpnp_coincell_driver = {
|
static struct platform_driver qpnp_coincell_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_COINCELL_DRIVER_NAME,
|
.name = QPNP_COINCELL_DRIVER_NAME,
|
||||||
.of_match_table = qpnp_coincell_match_table,
|
.of_match_table = qpnp_coincell_match_table,
|
||||||
|
@ -251,12 +268,12 @@ static struct spmi_driver qpnp_coincell_driver = {
|
||||||
|
|
||||||
static int __init qpnp_coincell_init(void)
|
static int __init qpnp_coincell_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_coincell_driver);
|
return platform_driver_register(&qpnp_coincell_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_coincell_exit(void)
|
static void __exit qpnp_coincell_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_coincell_driver);
|
platform_driver_unregister(&qpnp_coincell_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("QPNP PMIC coincell charger driver");
|
MODULE_DESCRIPTION("QPNP PMIC coincell charger driver");
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/hrtimer.h>
|
#include <linux/hrtimer.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/qpnp/pwm.h>
|
#include <linux/qpnp/pwm.h>
|
||||||
|
@ -280,56 +282,57 @@ struct qpnp_pwm_info {
|
||||||
* @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
|
* @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
|
||||||
*/
|
*/
|
||||||
struct qpnp_hap {
|
struct qpnp_hap {
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
struct regulator *vcc_pon;
|
struct regmap *regmap;
|
||||||
struct hrtimer hap_timer;
|
struct regulator *vcc_pon;
|
||||||
struct hrtimer auto_res_err_poll_timer;
|
struct hrtimer hap_timer;
|
||||||
struct timed_output_dev timed_dev;
|
struct hrtimer auto_res_err_poll_timer;
|
||||||
struct work_struct work;
|
struct timed_output_dev timed_dev;
|
||||||
struct work_struct auto_res_err_work;
|
struct work_struct work;
|
||||||
struct delayed_work sc_work;
|
struct work_struct auto_res_err_work;
|
||||||
struct hrtimer hap_test_timer;
|
struct delayed_work sc_work;
|
||||||
struct work_struct test_work;
|
struct hrtimer hap_test_timer;
|
||||||
struct qpnp_pwm_info pwm_info;
|
struct work_struct test_work;
|
||||||
struct mutex lock;
|
struct qpnp_pwm_info pwm_info;
|
||||||
struct mutex wf_lock;
|
struct mutex lock;
|
||||||
struct completion completion;
|
struct mutex wf_lock;
|
||||||
enum qpnp_hap_mode play_mode;
|
struct completion completion;
|
||||||
enum qpnp_hap_auto_res_mode auto_res_mode;
|
enum qpnp_hap_mode play_mode;
|
||||||
enum qpnp_hap_high_z lra_high_z;
|
enum qpnp_hap_auto_res_mode auto_res_mode;
|
||||||
u32 timeout_ms;
|
enum qpnp_hap_high_z lra_high_z;
|
||||||
u32 vmax_mv;
|
u32 timeout_ms;
|
||||||
u32 ilim_ma;
|
u32 vmax_mv;
|
||||||
u32 sc_deb_cycles;
|
u32 ilim_ma;
|
||||||
u32 int_pwm_freq_khz;
|
u32 sc_deb_cycles;
|
||||||
u32 wave_play_rate_us;
|
u32 int_pwm_freq_khz;
|
||||||
u32 ext_pwm_freq_khz;
|
u32 wave_play_rate_us;
|
||||||
u32 wave_rep_cnt;
|
u32 ext_pwm_freq_khz;
|
||||||
u32 wave_s_rep_cnt;
|
u32 wave_rep_cnt;
|
||||||
u32 play_irq;
|
u32 wave_s_rep_cnt;
|
||||||
u32 sc_irq;
|
u32 play_irq;
|
||||||
u16 base;
|
u32 sc_irq;
|
||||||
u8 act_type;
|
u16 base;
|
||||||
u8 wave_shape;
|
u8 act_type;
|
||||||
|
u8 wave_shape;
|
||||||
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
||||||
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
|
||||||
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
|
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
|
||||||
u8 reg_en_ctl;
|
u8 reg_en_ctl;
|
||||||
u8 reg_play;
|
u8 reg_play;
|
||||||
u8 lra_res_cal_period;
|
u8 lra_res_cal_period;
|
||||||
u8 sc_duration;
|
u8 sc_duration;
|
||||||
u8 ext_pwm_dtest_line;
|
u8 ext_pwm_dtest_line;
|
||||||
bool state;
|
bool state;
|
||||||
bool use_play_irq;
|
bool use_play_irq;
|
||||||
bool use_sc_irq;
|
bool use_sc_irq;
|
||||||
bool manage_pon_supply;
|
bool manage_pon_supply;
|
||||||
bool wf_update;
|
bool wf_update;
|
||||||
bool pwm_cfg_state;
|
bool pwm_cfg_state;
|
||||||
bool buffer_cfg_state;
|
bool buffer_cfg_state;
|
||||||
bool en_brake;
|
bool en_brake;
|
||||||
bool sup_brake_pat;
|
bool sup_brake_pat;
|
||||||
bool correct_lra_drive_freq;
|
bool correct_lra_drive_freq;
|
||||||
bool misc_trim_error_rc19p2_clk_reg_present;
|
bool misc_trim_error_rc19p2_clk_reg_present;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct qpnp_hap *ghap;
|
static struct qpnp_hap *ghap;
|
||||||
|
@ -338,13 +341,13 @@ static struct qpnp_hap *ghap;
|
||||||
static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
|
static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(hap->spmi->ctrl, hap->spmi->sid,
|
rc = regmap_read(hap->regmap, addr, &val);
|
||||||
addr, data, 1);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Error reading address: %X - ret %X\n", addr, rc);
|
"Error reading address: %X - ret %X\n", addr, rc);
|
||||||
|
*data = (u8)val;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,13 +356,12 @@ static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(hap->spmi->ctrl, hap->spmi->sid,
|
rc = regmap_write(hap->regmap, addr, *data);
|
||||||
addr, data, 1);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Error writing address: %X - ret %X\n", addr, rc);
|
"Error writing address: %X - ret %X\n", addr, rc);
|
||||||
|
|
||||||
dev_dbg(&hap->spmi->dev, "write: HAP_0x%x = 0x%x\n", addr, *data);
|
dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +412,7 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
|
||||||
rc = qpnp_hap_read_reg(hap, &val,
|
rc = qpnp_hap_read_reg(hap, &val,
|
||||||
QPNP_HAP_STATUS(hap->base));
|
QPNP_HAP_STATUS(hap->base));
|
||||||
|
|
||||||
dev_dbg(&hap->spmi->dev, "HAP_STATUS=0x%x\n", val);
|
dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val);
|
||||||
|
|
||||||
/* wait for QPNP_HAP_CYCLS cycles of play rate */
|
/* wait for QPNP_HAP_CYCLS cycles of play rate */
|
||||||
if (val & QPNP_HAP_STATUS_BUSY) {
|
if (val & QPNP_HAP_STATUS_BUSY) {
|
||||||
|
@ -423,7 +425,7 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >= QPNP_HAP_MAX_RETRIES)
|
if (i >= QPNP_HAP_MAX_RETRIES)
|
||||||
dev_dbg(&hap->spmi->dev,
|
dev_dbg(&hap->pdev->dev,
|
||||||
"Haptics Busy. Force disable\n");
|
"Haptics Busy. Force disable\n");
|
||||||
|
|
||||||
val &= ~QPNP_HAP_EN;
|
val &= ~QPNP_HAP_EN;
|
||||||
|
@ -517,7 +519,7 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
|
||||||
u8 disable_haptics = 0x00;
|
u8 disable_haptics = 0x00;
|
||||||
u8 val;
|
u8 val;
|
||||||
|
|
||||||
dev_dbg(&hap->spmi->dev, "Short circuit detected\n");
|
dev_dbg(&hap->pdev->dev, "Short circuit detected\n");
|
||||||
|
|
||||||
if (hap->sc_duration < SC_MAX_DURATION) {
|
if (hap->sc_duration < SC_MAX_DURATION) {
|
||||||
qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
|
qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
|
||||||
|
@ -532,7 +534,7 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
|
||||||
*/
|
*/
|
||||||
rc = qpnp_hap_write_reg(hap, &disable_haptics,
|
rc = qpnp_hap_write_reg(hap, &disable_haptics,
|
||||||
QPNP_HAP_EN_CTL_REG(hap->base));
|
QPNP_HAP_EN_CTL_REG(hap->base));
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Haptics disabled permanently due to short circuit\n");
|
"Haptics disabled permanently due to short circuit\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,12 +584,12 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
|
||||||
|
|
||||||
/* setup play irq */
|
/* setup play irq */
|
||||||
if (hap->use_play_irq) {
|
if (hap->use_play_irq) {
|
||||||
rc = devm_request_threaded_irq(&hap->spmi->dev, hap->play_irq,
|
rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq,
|
||||||
NULL, qpnp_hap_play_irq,
|
NULL, qpnp_hap_play_irq,
|
||||||
QPNP_IRQ_FLAGS,
|
QPNP_IRQ_FLAGS,
|
||||||
"qpnp_play_irq", hap);
|
"qpnp_play_irq", hap);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Unable to request play(%d) IRQ(err:%d)\n",
|
"Unable to request play(%d) IRQ(err:%d)\n",
|
||||||
hap->play_irq, rc);
|
hap->play_irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -638,7 +640,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
|
||||||
return rc;
|
return rc;
|
||||||
if (!hap->ext_pwm_dtest_line ||
|
if (!hap->ext_pwm_dtest_line ||
|
||||||
hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) {
|
hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) {
|
||||||
dev_err(&hap->spmi->dev, "invalid dtest line\n");
|
dev_err(&hap->pdev->dev, "invalid dtest line\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,7 +663,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
|
||||||
hap->pwm_info.duty_us * NSEC_PER_USEC,
|
hap->pwm_info.duty_us * NSEC_PER_USEC,
|
||||||
hap->pwm_info.period_us * NSEC_PER_USEC);
|
hap->pwm_info.period_us * NSEC_PER_USEC);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&hap->spmi->dev, "hap pwm config failed\n");
|
dev_err(&hap->pdev->dev, "hap pwm config failed\n");
|
||||||
pwm_free(hap->pwm_info.pwm_dev);
|
pwm_free(hap->pwm_info.pwm_dev);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -742,48 +744,47 @@ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap)
|
||||||
/* DT parsing api for buffer mode */
|
/* DT parsing api for buffer mode */
|
||||||
static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
|
static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = hap->spmi;
|
struct platform_device *pdev = hap->pdev;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN;
|
hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,wave-rep-cnt", &temp);
|
"qcom,wave-rep-cnt", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->wave_rep_cnt = temp;
|
hap->wave_rep_cnt = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read rep cnt\n");
|
dev_err(&pdev->dev, "Unable to read rep cnt\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MIN;
|
hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MIN;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,wave-samp-rep-cnt", &temp);
|
"qcom,wave-samp-rep-cnt", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->wave_s_rep_cnt = temp;
|
hap->wave_s_rep_cnt = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read samp rep cnt\n");
|
dev_err(&pdev->dev, "Unable to read samp rep cnt\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
prop = of_find_property(spmi->dev.of_node,
|
prop = of_find_property(pdev->dev.of_node,
|
||||||
"qcom,wave-samples", &temp);
|
"qcom,wave-samples", &temp);
|
||||||
if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) {
|
if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) {
|
||||||
dev_err(&spmi->dev, "Invalid wave samples, use default");
|
dev_err(&pdev->dev, "Invalid wave samples, use default");
|
||||||
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++)
|
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++)
|
||||||
hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX;
|
hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX;
|
||||||
} else {
|
} else {
|
||||||
memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN);
|
memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->use_play_irq = of_property_read_bool(spmi->dev.of_node,
|
hap->use_play_irq = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,use-play-irq");
|
"qcom,use-play-irq");
|
||||||
if (hap->use_play_irq) {
|
if (hap->use_play_irq) {
|
||||||
hap->play_irq = spmi_get_irq_byname(hap->spmi,
|
hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq");
|
||||||
NULL, "play-irq");
|
|
||||||
if (hap->play_irq < 0) {
|
if (hap->play_irq < 0) {
|
||||||
dev_err(&spmi->dev, "Unable to get play irq\n");
|
dev_err(&pdev->dev, "Unable to get play irq\n");
|
||||||
return hap->play_irq;
|
return hap->play_irq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,51 +795,49 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
|
||||||
/* DT parsing api for PWM mode */
|
/* DT parsing api for PWM mode */
|
||||||
static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
|
static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = hap->spmi;
|
struct platform_device *pdev = hap->pdev;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ;
|
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,ext-pwm-freq-khz", &temp);
|
"qcom,ext-pwm-freq-khz", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->ext_pwm_freq_khz = temp;
|
hap->ext_pwm_freq_khz = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read ext pwm freq\n");
|
dev_err(&pdev->dev, "Unable to read ext pwm freq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,pwm-channel", &temp);
|
"qcom,pwm-channel", &temp);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
hap->pwm_info.pwm_channel = temp;
|
hap->pwm_info.pwm_channel = temp;
|
||||||
else
|
else
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
hap->pwm_info.pwm_dev = of_pwm_get(spmi->dev.of_node, NULL);
|
hap->pwm_info.pwm_dev = of_pwm_get(pdev->dev.of_node, NULL);
|
||||||
|
|
||||||
if (IS_ERR(hap->pwm_info.pwm_dev)) {
|
if (IS_ERR(hap->pwm_info.pwm_dev)) {
|
||||||
rc = PTR_ERR(hap->pwm_info.pwm_dev);
|
rc = PTR_ERR(hap->pwm_info.pwm_dev);
|
||||||
dev_err(&spmi->dev, "Cannot get PWM device rc:(%d)\n", rc);
|
dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc);
|
||||||
hap->pwm_info.pwm_dev = NULL;
|
hap->pwm_info.pwm_dev = NULL;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node, "qcom,period-us", &temp);
|
||||||
"qcom,period-us", &temp);
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
hap->pwm_info.period_us = temp;
|
hap->pwm_info.period_us = temp;
|
||||||
else
|
else
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node, "qcom,duty-us", &temp);
|
||||||
"qcom,duty-us", &temp);
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
hap->pwm_info.duty_us = temp;
|
hap->pwm_info.duty_us = temp;
|
||||||
else
|
else
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,ext-pwm-dtest-line", &temp);
|
"qcom,ext-pwm-dtest-line", &temp);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
hap->ext_pwm_dtest_line = temp;
|
hap->ext_pwm_dtest_line = temp;
|
||||||
|
@ -1587,7 +1586,7 @@ int qpnp_hap_play_byte(u8 data, bool on)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hap->play_mode != QPNP_HAP_PWM) {
|
if (hap->play_mode != QPNP_HAP_PWM) {
|
||||||
dev_err(&hap->spmi->dev, "only PWM mode is supported\n");
|
dev_err(&hap->pdev->dev, "only PWM mode is supported\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1624,7 +1623,8 @@ int qpnp_hap_play_byte(u8 data, bool on)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
dev_dbg(&hap->spmi->dev, "data=0x%x duty_per=%d\n", data, duty_percent);
|
dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data,
|
||||||
|
duty_percent);
|
||||||
|
|
||||||
rc = qpnp_hap_set(hap, true);
|
rc = qpnp_hap_set(hap, true);
|
||||||
|
|
||||||
|
@ -1723,8 +1723,9 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
|
||||||
/* Configuration api for haptics registers */
|
/* Configuration api for haptics registers */
|
||||||
static int qpnp_hap_config(struct qpnp_hap *hap)
|
static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
u8 reg = 0, error_code = 0, unlock_val, error_value;
|
u8 reg = 0, unlock_val, error_value;
|
||||||
int rc, i, temp;
|
int rc, i, temp;
|
||||||
|
uint error_code = 0;
|
||||||
|
|
||||||
/* Configure the ACTUATOR TYPE register */
|
/* Configure the ACTUATOR TYPE register */
|
||||||
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base));
|
rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base));
|
||||||
|
@ -1862,15 +1863,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
|
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
|
||||||
&& hap->misc_trim_error_rc19p2_clk_reg_present) {
|
&& hap->misc_trim_error_rc19p2_clk_reg_present) {
|
||||||
unlock_val = MISC_SEC_UNLOCK;
|
unlock_val = MISC_SEC_UNLOCK;
|
||||||
rc = spmi_ext_register_writel(hap->spmi->ctrl,
|
rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
|
||||||
PMI8950_MISC_SID, MISC_SEC_ACCESS,
|
|
||||||
&unlock_val, 1);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Unable to do SEC_ACCESS rc:%d\n", rc);
|
"Unable to do SEC_ACCESS rc:%d\n", rc);
|
||||||
|
|
||||||
spmi_ext_register_readl(hap->spmi->ctrl, PMI8950_MISC_SID,
|
regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
|
||||||
MISC_TRIM_ERROR_RC19P2_CLK, &error_code, 1);
|
&error_code);
|
||||||
|
|
||||||
error_value = (error_code & 0x0F) * 7;
|
error_value = (error_code & 0x0F) * 7;
|
||||||
|
|
||||||
|
@ -1947,12 +1946,12 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
|
|
||||||
/* setup short circuit irq */
|
/* setup short circuit irq */
|
||||||
if (hap->use_sc_irq) {
|
if (hap->use_sc_irq) {
|
||||||
rc = devm_request_threaded_irq(&hap->spmi->dev, hap->sc_irq,
|
rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq,
|
||||||
NULL, qpnp_hap_sc_irq,
|
NULL, qpnp_hap_sc_irq,
|
||||||
QPNP_IRQ_FLAGS,
|
QPNP_IRQ_FLAGS,
|
||||||
"qpnp_sc_irq", hap);
|
"qpnp_sc_irq", hap);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&hap->spmi->dev,
|
dev_err(&hap->pdev->dev,
|
||||||
"Unable to request sc(%d) IRQ(err:%d)\n",
|
"Unable to request sc(%d) IRQ(err:%d)\n",
|
||||||
hap->sc_irq, rc);
|
hap->sc_irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1967,24 +1966,24 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
|
||||||
/* DT parsing for haptics parameters */
|
/* DT parsing for haptics parameters */
|
||||||
static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
{
|
{
|
||||||
struct spmi_device *spmi = hap->spmi;
|
struct platform_device *pdev = hap->pdev;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
const char *temp_str;
|
const char *temp_str;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX;
|
hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,timeout-ms", &temp);
|
"qcom,timeout-ms", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->timeout_ms = temp;
|
hap->timeout_ms = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read timeout\n");
|
dev_err(&pdev->dev, "Unable to read timeout\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->act_type = QPNP_HAP_LRA;
|
hap->act_type = QPNP_HAP_LRA;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,actuator-type", &temp_str);
|
"qcom,actuator-type", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "erm") == 0)
|
if (strcmp(temp_str, "erm") == 0)
|
||||||
|
@ -1992,17 +1991,17 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
else if (strcmp(temp_str, "lra") == 0)
|
else if (strcmp(temp_str, "lra") == 0)
|
||||||
hap->act_type = QPNP_HAP_LRA;
|
hap->act_type = QPNP_HAP_LRA;
|
||||||
else {
|
else {
|
||||||
dev_err(&spmi->dev, "Invalid actuator type\n");
|
dev_err(&pdev->dev, "Invalid actuator type\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read actuator type\n");
|
dev_err(&pdev->dev, "Unable to read actuator type\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hap->act_type == QPNP_HAP_LRA) {
|
if (hap->act_type == QPNP_HAP_LRA) {
|
||||||
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
|
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,lra-auto-res-mode", &temp_str);
|
"qcom,lra-auto-res-mode", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "none") == 0)
|
if (strcmp(temp_str, "none") == 0)
|
||||||
|
@ -2016,12 +2015,12 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
else
|
else
|
||||||
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
|
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read auto res mode\n");
|
dev_err(&pdev->dev, "Unable to read auto res mode\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
|
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,lra-high-z", &temp_str);
|
"qcom,lra-high-z", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "none") == 0)
|
if (strcmp(temp_str, "none") == 0)
|
||||||
|
@ -2033,30 +2032,30 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
else
|
else
|
||||||
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
|
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read LRA high-z\n");
|
dev_err(&pdev->dev, "Unable to read LRA high-z\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
|
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,lra-res-cal-period", &temp);
|
"qcom,lra-res-cal-period", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->lra_res_cal_period = temp;
|
hap->lra_res_cal_period = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read cal period\n");
|
dev_err(&pdev->dev, "Unable to read cal period\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->correct_lra_drive_freq =
|
hap->correct_lra_drive_freq =
|
||||||
of_property_read_bool(spmi->dev.of_node,
|
of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,correct-lra-drive-freq");
|
"qcom,correct-lra-drive-freq");
|
||||||
|
|
||||||
hap->misc_trim_error_rc19p2_clk_reg_present =
|
hap->misc_trim_error_rc19p2_clk_reg_present =
|
||||||
of_property_read_bool(spmi->dev.of_node,
|
of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,misc-trim-error-rc19p2-clk-reg-present");
|
"qcom,misc-trim-error-rc19p2-clk-reg-present");
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,play-mode", &temp_str);
|
"qcom,play-mode", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "direct") == 0)
|
if (strcmp(temp_str, "direct") == 0)
|
||||||
|
@ -2068,56 +2067,54 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
else if (strcmp(temp_str, "audio") == 0)
|
else if (strcmp(temp_str, "audio") == 0)
|
||||||
hap->play_mode = QPNP_HAP_AUDIO;
|
hap->play_mode = QPNP_HAP_AUDIO;
|
||||||
else {
|
else {
|
||||||
dev_err(&spmi->dev, "Invalid play mode\n");
|
dev_err(&pdev->dev, "Invalid play mode\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_err(&spmi->dev, "Unable to read play mode\n");
|
dev_err(&pdev->dev, "Unable to read play mode\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV;
|
hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node, "qcom,vmax-mv", &temp);
|
||||||
"qcom,vmax-mv", &temp);
|
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->vmax_mv = temp;
|
hap->vmax_mv = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read vmax\n");
|
dev_err(&pdev->dev, "Unable to read vmax\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->ilim_ma = QPNP_HAP_ILIM_MIN_MV;
|
hap->ilim_ma = QPNP_HAP_ILIM_MIN_MV;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node, "qcom,ilim-ma", &temp);
|
||||||
"qcom,ilim-ma", &temp);
|
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->ilim_ma = temp;
|
hap->ilim_ma = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read ILim\n");
|
dev_err(&pdev->dev, "Unable to read ILim\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->sc_deb_cycles = QPNP_HAP_DEF_SC_DEB_CYCLES;
|
hap->sc_deb_cycles = QPNP_HAP_DEF_SC_DEB_CYCLES;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,sc-deb-cycles", &temp);
|
"qcom,sc-deb-cycles", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->sc_deb_cycles = temp;
|
hap->sc_deb_cycles = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read sc debounce\n");
|
dev_err(&pdev->dev, "Unable to read sc debounce\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
|
hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,int-pwm-freq-khz", &temp);
|
"qcom,int-pwm-freq-khz", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->int_pwm_freq_khz = temp;
|
hap->int_pwm_freq_khz = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read int pwm freq\n");
|
dev_err(&pdev->dev, "Unable to read int pwm freq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
|
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,wave-shape", &temp_str);
|
"qcom,wave-shape", &temp_str);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp(temp_str, "sine") == 0)
|
if (strcmp(temp_str, "sine") == 0)
|
||||||
|
@ -2125,21 +2122,21 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
else if (strcmp(temp_str, "square") == 0)
|
else if (strcmp(temp_str, "square") == 0)
|
||||||
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
|
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
|
||||||
else {
|
else {
|
||||||
dev_err(&spmi->dev, "Unsupported wav shape\n");
|
dev_err(&pdev->dev, "Unsupported wav shape\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read wav shape\n");
|
dev_err(&pdev->dev, "Unable to read wav shape\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->wave_play_rate_us = QPNP_HAP_DEF_WAVE_PLAY_RATE_US;
|
hap->wave_play_rate_us = QPNP_HAP_DEF_WAVE_PLAY_RATE_US;
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,wave-play-rate-us", &temp);
|
"qcom,wave-play-rate-us", &temp);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
hap->wave_play_rate_us = temp;
|
hap->wave_play_rate_us = temp;
|
||||||
} else if (rc != -EINVAL) {
|
} else if (rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev, "Unable to read play rate\n");
|
dev_err(&pdev->dev, "Unable to read play rate\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2151,16 +2148,16 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
hap->en_brake = of_property_read_bool(spmi->dev.of_node,
|
hap->en_brake = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,en-brake");
|
"qcom,en-brake");
|
||||||
|
|
||||||
if (hap->en_brake) {
|
if (hap->en_brake) {
|
||||||
prop = of_find_property(spmi->dev.of_node,
|
prop = of_find_property(pdev->dev.of_node,
|
||||||
"qcom,brake-pattern", &temp);
|
"qcom,brake-pattern", &temp);
|
||||||
if (!prop) {
|
if (!prop) {
|
||||||
dev_info(&spmi->dev, "brake pattern not found");
|
dev_info(&pdev->dev, "brake pattern not found");
|
||||||
} else if (temp != QPNP_HAP_BRAKE_PAT_LEN) {
|
} else if (temp != QPNP_HAP_BRAKE_PAT_LEN) {
|
||||||
dev_err(&spmi->dev, "Invalid len of brake pattern\n");
|
dev_err(&pdev->dev, "Invalid len of brake pattern\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else {
|
} else {
|
||||||
hap->sup_brake_pat = true;
|
hap->sup_brake_pat = true;
|
||||||
|
@ -2169,54 +2166,60 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hap->use_sc_irq = of_property_read_bool(spmi->dev.of_node,
|
hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,use-sc-irq");
|
"qcom,use-sc-irq");
|
||||||
if (hap->use_sc_irq) {
|
if (hap->use_sc_irq) {
|
||||||
hap->sc_irq = spmi_get_irq_byname(hap->spmi,
|
hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq");
|
||||||
NULL, "sc-irq");
|
|
||||||
if (hap->sc_irq < 0) {
|
if (hap->sc_irq < 0) {
|
||||||
dev_err(&spmi->dev, "Unable to get sc irq\n");
|
dev_err(&pdev->dev, "Unable to get sc irq\n");
|
||||||
return hap->sc_irq;
|
return hap->sc_irq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_find_property(spmi->dev.of_node, "vcc_pon-supply", NULL))
|
if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL))
|
||||||
hap->manage_pon_supply = true;
|
hap->manage_pon_supply = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_haptic_probe(struct spmi_device *spmi)
|
static int qpnp_haptic_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_hap *hap;
|
struct qpnp_hap *hap;
|
||||||
struct resource *hap_resource;
|
unsigned int base;
|
||||||
struct regulator *vcc_pon;
|
struct regulator *vcc_pon;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
hap = devm_kzalloc(&spmi->dev, sizeof(*hap), GFP_KERNEL);
|
hap = devm_kzalloc(&pdev->dev, sizeof(*hap), GFP_KERNEL);
|
||||||
if (!hap)
|
if (!hap)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
hap->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!hap->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
hap->spmi = spmi;
|
hap->pdev = pdev;
|
||||||
|
|
||||||
hap_resource = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
if (!hap_resource) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "Unable to get haptic base address\n");
|
dev_err(&pdev->dev,
|
||||||
return -EINVAL;
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
hap->base = hap_resource->start;
|
hap->base = base;
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, hap);
|
dev_set_drvdata(&pdev->dev, hap);
|
||||||
|
|
||||||
rc = qpnp_hap_parse_dt(hap);
|
rc = qpnp_hap_parse_dt(hap);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "DT parsing failed\n");
|
dev_err(&pdev->dev, "DT parsing failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_hap_config(hap);
|
rc = qpnp_hap_config(hap);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "hap config failed\n");
|
dev_err(&pdev->dev, "hap config failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2245,7 +2248,7 @@ static int qpnp_haptic_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = timed_output_dev_register(&hap->timed_dev);
|
rc = timed_output_dev_register(&hap->timed_dev);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "timed_output registration failed\n");
|
dev_err(&pdev->dev, "timed_output registration failed\n");
|
||||||
goto timed_output_fail;
|
goto timed_output_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2253,16 +2256,16 @@ static int qpnp_haptic_probe(struct spmi_device *spmi)
|
||||||
rc = sysfs_create_file(&hap->timed_dev.dev->kobj,
|
rc = sysfs_create_file(&hap->timed_dev.dev->kobj,
|
||||||
&qpnp_hap_attrs[i].attr);
|
&qpnp_hap_attrs[i].attr);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "sysfs creation failed\n");
|
dev_err(&pdev->dev, "sysfs creation failed\n");
|
||||||
goto sysfs_fail;
|
goto sysfs_fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hap->manage_pon_supply) {
|
if (hap->manage_pon_supply) {
|
||||||
vcc_pon = regulator_get(&spmi->dev, "vcc_pon");
|
vcc_pon = regulator_get(&pdev->dev, "vcc_pon");
|
||||||
if (IS_ERR(vcc_pon)) {
|
if (IS_ERR(vcc_pon)) {
|
||||||
rc = PTR_ERR(vcc_pon);
|
rc = PTR_ERR(vcc_pon);
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"regulator get failed vcc_pon rc=%d\n", rc);
|
"regulator get failed vcc_pon rc=%d\n", rc);
|
||||||
goto sysfs_fail;
|
goto sysfs_fail;
|
||||||
}
|
}
|
||||||
|
@ -2289,9 +2292,9 @@ timed_output_fail:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_haptic_remove(struct spmi_device *spmi)
|
static int qpnp_haptic_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_hap *hap = dev_get_drvdata(&spmi->dev);
|
struct qpnp_hap *hap = dev_get_drvdata(&pdev->dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(qpnp_hap_attrs); i++)
|
for (i = 0; i < ARRAY_SIZE(qpnp_hap_attrs); i++)
|
||||||
|
@ -2316,11 +2319,11 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_haptic_driver = {
|
static struct platform_driver qpnp_haptic_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-haptic",
|
.name = "qcom,qpnp-haptic",
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
.pm = &qpnp_haptic_pm_ops,
|
.pm = &qpnp_haptic_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = qpnp_haptic_probe,
|
.probe = qpnp_haptic_probe,
|
||||||
.remove = qpnp_haptic_remove,
|
.remove = qpnp_haptic_remove,
|
||||||
|
@ -2328,13 +2331,13 @@ static struct spmi_driver qpnp_haptic_driver = {
|
||||||
|
|
||||||
static int __init qpnp_haptic_init(void)
|
static int __init qpnp_haptic_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_haptic_driver);
|
return platform_driver_register(&qpnp_haptic_driver);
|
||||||
}
|
}
|
||||||
module_init(qpnp_haptic_init);
|
module_init(qpnp_haptic_init);
|
||||||
|
|
||||||
static void __exit qpnp_haptic_exit(void)
|
static void __exit qpnp_haptic_exit(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_unregister(&qpnp_haptic_driver);
|
return platform_driver_unregister(&qpnp_haptic_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_haptic_exit);
|
module_exit(qpnp_haptic_exit);
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,8 +13,11 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/qpnp/qpnp-revid.h>
|
#include <linux/qpnp/qpnp-revid.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#define REVID_REVISION1 0x0
|
#define REVID_REVISION1 0x0
|
||||||
#define REVID_REVISION2 0x1
|
#define REVID_REVISION2 0x1
|
||||||
|
@ -66,17 +69,17 @@ static struct of_device_id qpnp_revid_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static u8 qpnp_read_byte(struct spmi_device *spmi, u16 addr)
|
static u8 qpnp_read_byte(struct regmap *regmap, u16 addr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 val;
|
int val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, &val, 1);
|
rc = regmap_read(regmap, addr, &val);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SPMI read failed rc=%d\n", rc);
|
pr_err("read failed rc=%d\n", rc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return val;
|
return (u8)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,51 +148,60 @@ static size_t build_pmic_string(char *buf, size_t n, int sid,
|
||||||
|
|
||||||
#define PMIC_PERIPHERAL_TYPE 0x51
|
#define PMIC_PERIPHERAL_TYPE 0x51
|
||||||
#define PMIC_STRING_MAXLENGTH 80
|
#define PMIC_STRING_MAXLENGTH 80
|
||||||
static int qpnp_revid_probe(struct spmi_device *spmi)
|
static int qpnp_revid_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
|
u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
|
||||||
u8 option1, option2, option3, option4, spare0;
|
u8 option1, option2, option3, option4, spare0;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
|
int rc;
|
||||||
char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
|
char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
|
||||||
struct revid_chip *revid_chip;
|
struct revid_chip *revid_chip;
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
if (!resource) {
|
if (!regmap) {
|
||||||
pr_err("Unable to get spmi resource for REVID\n");
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
pmic_type = qpnp_read_byte(spmi, resource->start + REVID_TYPE);
|
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
pmic_type = qpnp_read_byte(regmap, base + REVID_TYPE);
|
||||||
if (pmic_type != PMIC_PERIPHERAL_TYPE) {
|
if (pmic_type != PMIC_PERIPHERAL_TYPE) {
|
||||||
pr_err("Invalid REVID peripheral type: %02X\n", pmic_type);
|
pr_err("Invalid REVID peripheral type: %02X\n", pmic_type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rev1 = qpnp_read_byte(spmi, resource->start + REVID_REVISION1);
|
rev1 = qpnp_read_byte(regmap, base + REVID_REVISION1);
|
||||||
rev2 = qpnp_read_byte(spmi, resource->start + REVID_REVISION2);
|
rev2 = qpnp_read_byte(regmap, base + REVID_REVISION2);
|
||||||
rev3 = qpnp_read_byte(spmi, resource->start + REVID_REVISION3);
|
rev3 = qpnp_read_byte(regmap, base + REVID_REVISION3);
|
||||||
rev4 = qpnp_read_byte(spmi, resource->start + REVID_REVISION4);
|
rev4 = qpnp_read_byte(regmap, base + REVID_REVISION4);
|
||||||
|
|
||||||
pmic_subtype = qpnp_read_byte(spmi, resource->start + REVID_SUBTYPE);
|
pmic_subtype = qpnp_read_byte(regmap, base + REVID_SUBTYPE);
|
||||||
if (pmic_subtype != PMD9655_PERIPHERAL_SUBTYPE)
|
if (pmic_subtype != PMD9655_PERIPHERAL_SUBTYPE)
|
||||||
pmic_status = qpnp_read_byte(spmi,
|
pmic_status = qpnp_read_byte(regmap, base + REVID_STATUS1);
|
||||||
resource->start + REVID_STATUS1);
|
|
||||||
else
|
else
|
||||||
pmic_status = 0;
|
pmic_status = 0;
|
||||||
|
|
||||||
/* special case for PMI8937 */
|
/* special case for PMI8937 */
|
||||||
if (pmic_subtype == PMI8950_PERIPHERAL_SUBTYPE) {
|
if (pmic_subtype == PMI8950_PERIPHERAL_SUBTYPE) {
|
||||||
/* read spare register */
|
/* read spare register */
|
||||||
spare0 = qpnp_read_byte(spmi, resource->start + REVID_SPARE_0);
|
spare0 = qpnp_read_byte(regmap, base + REVID_SPARE_0);
|
||||||
if (spare0)
|
if (spare0)
|
||||||
pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
|
pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
revid_chip = devm_kzalloc(&spmi->dev, sizeof(struct revid_chip),
|
revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!revid_chip)
|
if (!revid_chip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
revid_chip->dev_node = spmi->dev.of_node;
|
revid_chip->dev_node = pdev->dev.of_node;
|
||||||
revid_chip->data.rev1 = rev1;
|
revid_chip->data.rev1 = rev1;
|
||||||
revid_chip->data.rev2 = rev2;
|
revid_chip->data.rev2 = rev2;
|
||||||
revid_chip->data.rev3 = rev3;
|
revid_chip->data.rev3 = rev3;
|
||||||
|
@ -211,14 +223,15 @@ static int qpnp_revid_probe(struct spmi_device *spmi)
|
||||||
option3 = (pmic_status >> 4) & 0x3;
|
option3 = (pmic_status >> 4) & 0x3;
|
||||||
option4 = (pmic_status >> 6) & 0x3;
|
option4 = (pmic_status >> 6) & 0x3;
|
||||||
|
|
||||||
build_pmic_string(pmic_string, PMIC_STRING_MAXLENGTH, spmi->sid,
|
build_pmic_string(pmic_string, PMIC_STRING_MAXLENGTH,
|
||||||
|
to_spmi_device(pdev->dev.parent)->usid,
|
||||||
pmic_subtype, rev1, rev2, rev3, rev4);
|
pmic_subtype, rev1, rev2, rev3, rev4);
|
||||||
pr_info("%s options: %d, %d, %d, %d\n",
|
pr_info("%s options: %d, %d, %d, %d\n",
|
||||||
pmic_string, option1, option2, option3, option4);
|
pmic_string, option1, option2, option3, option4);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct spmi_driver qpnp_revid_driver = {
|
static struct platform_driver qpnp_revid_driver = {
|
||||||
.probe = qpnp_revid_probe,
|
.probe = qpnp_revid_probe,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_REVID_DEV_NAME,
|
.name = QPNP_REVID_DEV_NAME,
|
||||||
|
@ -229,12 +242,12 @@ static struct spmi_driver qpnp_revid_driver = {
|
||||||
|
|
||||||
static int __init qpnp_revid_init(void)
|
static int __init qpnp_revid_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_revid_driver);
|
return platform_driver_register(&qpnp_revid_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_revid_exit(void)
|
static void __exit qpnp_revid_exit(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_unregister(&qpnp_revid_driver);
|
return platform_driver_unregister(&qpnp_revid_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
subsys_initcall(qpnp_revid_init);
|
subsys_initcall(qpnp_revid_init);
|
||||||
|
|
|
@ -449,6 +449,130 @@ config CHARGER_SMB347
|
||||||
Say Y to include support for Summit Microelectronics SMB347
|
Say Y to include support for Summit Microelectronics SMB347
|
||||||
Battery Charger.
|
Battery Charger.
|
||||||
|
|
||||||
|
config SMB349_USB_CHARGER
|
||||||
|
tristate "smb349 usb charger (with VBUS detection)"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to enable support for the SMB349 switching mode based charger.
|
||||||
|
The driver supports charging control (enable/disable) and
|
||||||
|
charge-current limiting. It also provides USB VBUS detection and
|
||||||
|
notification support. The driver controls SMB349 via I2C and
|
||||||
|
supports device-tree interface.
|
||||||
|
|
||||||
|
config SMB349_DUAL_CHARGER
|
||||||
|
tristate "smb349 dual charger"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to enable support for the SMB349 dual charger single path
|
||||||
|
switching mode charger. The driver supports charging in conjuction
|
||||||
|
with an external charger on the same path by providing hand-off
|
||||||
|
control and charge-current limiting. The driver controls SMB349
|
||||||
|
via I2C in STAT IRQ driven mode and supports device-tree interface.
|
||||||
|
|
||||||
|
config SMB1351_USB_CHARGER
|
||||||
|
tristate "smb1351 usb charger (with VBUS detection)"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to enable support for the SMB1351 switching mode based charger.
|
||||||
|
The driver supports charging control (enable/disable) and
|
||||||
|
charge-current limiting. It also provides USB VBUS detection and
|
||||||
|
notification support. The driver controls SMB1351 via I2C and
|
||||||
|
supports device-tree interface.
|
||||||
|
|
||||||
|
config SMB350_CHARGER
|
||||||
|
tristate "smb350 charger"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to enable battery charging by SMB350 switching mode based
|
||||||
|
external charger. The device supports stack-cell battery charging.
|
||||||
|
The driver configures the device volatile parameters
|
||||||
|
and the charger device works autonomously.
|
||||||
|
The driver supports charger-enable and charger-suspend/resume.
|
||||||
|
The driver reports the charger status via the power supply framework.
|
||||||
|
A charger status change triggers an IRQ via the device STAT pin.
|
||||||
|
|
||||||
|
config SMB135X_CHARGER
|
||||||
|
tristate "SMB135X Battery Charger"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to include support for SMB135X Battery Charger.
|
||||||
|
SMB135X is a dual path switching mode charger capable of charging
|
||||||
|
the battery with 3Amps of current.
|
||||||
|
The driver supports charger enable/disable.
|
||||||
|
The driver reports the charger status via the power supply framework.
|
||||||
|
A charger status change triggers an IRQ via the device STAT pin.
|
||||||
|
|
||||||
|
config SMB1360_CHARGER_FG
|
||||||
|
tristate "SMB1360 Charger and Fuel Guage"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to include support for SMB1360 Charger and Fuel Guage.
|
||||||
|
SMB1360 is a single path switching mode charger capable of charging
|
||||||
|
the battery with 1.5Amps of current. It supports a fuel gauge which
|
||||||
|
uses voltage and coloumb counting for state of charge reporting.
|
||||||
|
The driver reports the status via the power supply framework.
|
||||||
|
A status change triggers an IRQ via the device STAT pin.
|
||||||
|
|
||||||
|
config SMB358_CHARGER
|
||||||
|
tristate "SMB358 Battery Charger"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y to include support for SMB358 Battery Charger.
|
||||||
|
SMB358 is a single path switching mode charger capable of charging
|
||||||
|
the battery with 2Amps of current.
|
||||||
|
The driver supports charger enable/disable.
|
||||||
|
The driver reports the charger status via the power supply framework.
|
||||||
|
A charger status change triggers an IRQ via the device STAT pin.
|
||||||
|
|
||||||
|
config BATTERY_BQ28400
|
||||||
|
tristate "BQ28400 battery driver"
|
||||||
|
depends on I2C
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Say Y here to enable support for batteries with BQ28400 (I2C) chips.
|
||||||
|
The bq28400 Texas Instruments Inc device monitors the battery
|
||||||
|
charging/discharging status via Rsens resistor, typically 10 mohm.
|
||||||
|
It monitors the battery temperature via Thermistor.
|
||||||
|
The device monitors the battery level (Relative-State-Of-Charge).
|
||||||
|
The device is SBS compliant, providing battery info over I2C.
|
||||||
|
|
||||||
|
config QPNP_CHARGER
|
||||||
|
tristate "QPNP Charger driver"
|
||||||
|
depends on SPMI
|
||||||
|
depends on THERMAL_QPNP_ADC_TM
|
||||||
|
help
|
||||||
|
Say Y here to enable the switch mode battery charger
|
||||||
|
and boost device which supports USB detection and charging. The driver
|
||||||
|
also offers relevant information to userspace via the power supply
|
||||||
|
framework.
|
||||||
|
|
||||||
|
config QPNP_SMBCHARGER
|
||||||
|
tristate "QPNP SMB Charger driver"
|
||||||
|
depends on SPMI
|
||||||
|
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 SPMI
|
||||||
|
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 BATTERY_BCL
|
||||||
|
tristate "Battery Current Limit driver"
|
||||||
|
depends on THERMAL_MONITOR
|
||||||
|
help
|
||||||
|
Say Y here to enable support for battery current limit
|
||||||
|
device. The BCL driver will poll BMS if
|
||||||
|
thermal daemon enables BCL.
|
||||||
|
It will notify thermal daemon if IBat crosses Imax threshold.
|
||||||
|
|
||||||
config CHARGER_TPS65090
|
config CHARGER_TPS65090
|
||||||
tristate "TPS65090 battery charger driver"
|
tristate "TPS65090 battery charger driver"
|
||||||
depends on MFD_TPS65090
|
depends on MFD_TPS65090
|
||||||
|
@ -510,8 +634,7 @@ config AXP20X_POWER
|
||||||
|
|
||||||
config QPNP_SMBCHARGER
|
config QPNP_SMBCHARGER
|
||||||
tristate "QPNP SMB Charger driver"
|
tristate "QPNP SMB Charger driver"
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
help
|
help
|
||||||
Say Y here to enable the dual path switch mode battery charger which
|
Say Y here to enable the dual path switch mode battery charger which
|
||||||
supports USB detection and battery charging up to 3A.
|
supports USB detection and battery charging up to 3A.
|
||||||
|
@ -520,8 +643,7 @@ config QPNP_SMBCHARGER
|
||||||
|
|
||||||
config QPNP_FG
|
config QPNP_FG
|
||||||
tristate "QPNP fuel gauge driver"
|
tristate "QPNP fuel gauge driver"
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
help
|
help
|
||||||
Say Y here to enable the Fuel Gauge driver. This adds support for
|
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
|
battery fuel gauging and state of charge of battery connected to the
|
||||||
|
@ -559,7 +681,7 @@ config MSM_BCL_CTL
|
||||||
|
|
||||||
config MSM_BCL_PERIPHERAL_CTL
|
config MSM_BCL_PERIPHERAL_CTL
|
||||||
bool "BCL driver to control the PMIC BCL peripheral"
|
bool "BCL driver to control the PMIC BCL peripheral"
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on MSM_BCL_CTL
|
depends on MSM_BCL_CTL
|
||||||
help
|
help
|
||||||
Say Y here to enable this BCL PMIC peripheral driver. This driver
|
Say Y here to enable this BCL PMIC peripheral driver. This driver
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/msm_bcl.h>
|
#include <linux/msm_bcl.h>
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
|
@ -114,14 +116,14 @@ struct bcl_peripheral_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bcl_device {
|
struct bcl_device {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
uint16_t base_addr;
|
struct regmap *regmap;
|
||||||
uint16_t pon_spare_addr;
|
uint16_t base_addr;
|
||||||
uint8_t slave_id;
|
uint16_t pon_spare_addr;
|
||||||
int i_src;
|
int i_src;
|
||||||
struct bcl_peripheral_data param[BCL_PARAM_MAX];
|
struct bcl_peripheral_data param[BCL_PARAM_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct bcl_device *bcl_perph;
|
static struct bcl_device *bcl_perph;
|
||||||
|
@ -139,9 +141,8 @@ static int bcl_read_multi_register(int16_t reg_offset, uint8_t *data, int len)
|
||||||
pr_err("BCL device not initialized\n");
|
pr_err("BCL device not initialized\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = spmi_ext_register_readl(bcl_perph->spmi->ctrl,
|
ret = regmap_bulk_read(bcl_perph->regmap,
|
||||||
bcl_perph->slave_id, (bcl_perph->base_addr + reg_offset),
|
(bcl_perph->base_addr + reg_offset), data, len);
|
||||||
data, len);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("Error reading register %d. err:%d", reg_offset, ret);
|
pr_err("Error reading register %d. err:%d", reg_offset, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -171,9 +172,7 @@ static int bcl_write_general_register(int16_t reg_offset,
|
||||||
pr_err("BCL device not initialized\n");
|
pr_err("BCL device not initialized\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = spmi_ext_register_writel(bcl_perph->spmi->ctrl,
|
ret = regmap_write(bcl_perph->regmap, (base + reg_offset), *write_buf);
|
||||||
bcl_perph->slave_id, (base + reg_offset),
|
|
||||||
write_buf, 1);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("Error reading register %d. err:%d", reg_offset, ret);
|
pr_err("Error reading register %d. err:%d", reg_offset, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -773,28 +772,19 @@ exit_intr:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcl_get_devicetree_data(struct spmi_device *spmi)
|
static int bcl_get_devicetree_data(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret = 0, irq_num = 0, temp_val = 0;
|
int ret = 0, irq_num = 0, temp_val = 0;
|
||||||
struct resource *resource = NULL;
|
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
const __be32 *prop = NULL;
|
const __be32 *prop = NULL;
|
||||||
struct device_node *dev_node = spmi->dev.of_node;
|
struct device_node *dev_node = pdev->dev.of_node;
|
||||||
|
|
||||||
/* Get SPMI peripheral address */
|
prop = of_get_address_by_name(dev_node, "fg_user_adc", 0, 0);
|
||||||
resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
|
||||||
if (!resource) {
|
|
||||||
pr_err("No base address defined\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
bcl_perph->slave_id = spmi->sid;
|
|
||||||
prop = of_get_address_by_name(dev_node,
|
|
||||||
"fg_user_adc", 0, 0);
|
|
||||||
if (prop) {
|
if (prop) {
|
||||||
bcl_perph->base_addr = be32_to_cpu(*prop);
|
bcl_perph->base_addr = be32_to_cpu(*prop);
|
||||||
pr_debug("fg_user_adc@%04x\n", bcl_perph->base_addr);
|
pr_debug("fg_user_adc@%04x\n", bcl_perph->base_addr);
|
||||||
} else {
|
} else {
|
||||||
dev_err(&spmi->dev, "No fg_user_adc registers found\n");
|
dev_err(&pdev->dev, "No fg_user_adc registers found\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,16 +796,14 @@ static int bcl_get_devicetree_data(struct spmi_device *spmi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register SPMI peripheral interrupt */
|
/* Register SPMI peripheral interrupt */
|
||||||
irq_num = spmi_get_irq_byname(spmi, NULL,
|
irq_num = platform_get_irq_byname(pdev, BCL_VBAT_INT_NAME);
|
||||||
BCL_VBAT_INT_NAME);
|
|
||||||
if (irq_num < 0) {
|
if (irq_num < 0) {
|
||||||
pr_err("Invalid vbat IRQ\n");
|
pr_err("Invalid vbat IRQ\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto bcl_dev_exit;
|
goto bcl_dev_exit;
|
||||||
}
|
}
|
||||||
bcl_perph->param[BCL_PARAM_VOLTAGE].irq_num = irq_num;
|
bcl_perph->param[BCL_PARAM_VOLTAGE].irq_num = irq_num;
|
||||||
irq_num = spmi_get_irq_byname(spmi, NULL,
|
irq_num = platform_get_irq_byname(pdev, BCL_IBAT_INT_NAME);
|
||||||
BCL_IBAT_INT_NAME);
|
|
||||||
if (irq_num < 0) {
|
if (irq_num < 0) {
|
||||||
pr_err("Invalid ibat IRQ\n");
|
pr_err("Invalid ibat IRQ\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
|
@ -1009,20 +997,25 @@ update_data_exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcl_probe(struct spmi_device *spmi)
|
static int bcl_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
bcl_perph = devm_kzalloc(&spmi->dev, sizeof(struct bcl_device),
|
bcl_perph = devm_kzalloc(&pdev->dev, sizeof(struct bcl_device),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!bcl_perph) {
|
if (!bcl_perph) {
|
||||||
pr_err("Memory alloc failed\n");
|
pr_err("Memory alloc failed\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
bcl_perph->spmi = spmi;
|
bcl_perph->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
bcl_perph->dev = &(spmi->dev);
|
if (!bcl_perph->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
bcl_perph->pdev = pdev;
|
||||||
|
bcl_perph->dev = &(pdev->dev);
|
||||||
|
|
||||||
ret = bcl_get_devicetree_data(spmi);
|
ret = bcl_get_devicetree_data(pdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Device tree data fetch error. err:%d", ret);
|
pr_err("Device tree data fetch error. err:%d", ret);
|
||||||
goto bcl_probe_exit;
|
goto bcl_probe_exit;
|
||||||
|
@ -1038,7 +1031,7 @@ static int bcl_probe(struct spmi_device *spmi)
|
||||||
bcl_psy.set_property = bcl_psy_set_property;
|
bcl_psy.set_property = bcl_psy_set_property;
|
||||||
bcl_psy.num_properties = 0;
|
bcl_psy.num_properties = 0;
|
||||||
bcl_psy.external_power_changed = power_supply_callback;
|
bcl_psy.external_power_changed = power_supply_callback;
|
||||||
ret = power_supply_register(&spmi->dev, &bcl_psy);
|
ret = power_supply_register(&pdev->dev, &bcl_psy);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("Unable to register bcl_psy rc = %d\n", ret);
|
pr_err("Unable to register bcl_psy rc = %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1050,14 +1043,14 @@ static int bcl_probe(struct spmi_device *spmi)
|
||||||
goto bcl_probe_exit;
|
goto bcl_probe_exit;
|
||||||
}
|
}
|
||||||
mutex_lock(&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
mutex_lock(&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
||||||
ret = devm_request_threaded_irq(&spmi->dev,
|
ret = devm_request_threaded_irq(&pdev->dev,
|
||||||
bcl_perph->param[BCL_PARAM_VOLTAGE].irq_num,
|
bcl_perph->param[BCL_PARAM_VOLTAGE].irq_num,
|
||||||
NULL, bcl_handle_vbat,
|
NULL, bcl_handle_vbat,
|
||||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||||
"bcl_vbat_interrupt",
|
"bcl_vbat_interrupt",
|
||||||
&bcl_perph->param[BCL_PARAM_VOLTAGE]);
|
&bcl_perph->param[BCL_PARAM_VOLTAGE]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&spmi->dev, "Error requesting VBAT irq. err:%d", ret);
|
dev_err(&pdev->dev, "Error requesting VBAT irq. err:%d", ret);
|
||||||
mutex_unlock(
|
mutex_unlock(
|
||||||
&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
||||||
goto bcl_probe_exit;
|
goto bcl_probe_exit;
|
||||||
|
@ -1070,14 +1063,14 @@ static int bcl_probe(struct spmi_device *spmi)
|
||||||
mutex_unlock(&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
mutex_unlock(&bcl_perph->param[BCL_PARAM_VOLTAGE].state_trans_lock);
|
||||||
|
|
||||||
mutex_lock(&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
mutex_lock(&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
||||||
ret = devm_request_threaded_irq(&spmi->dev,
|
ret = devm_request_threaded_irq(&pdev->dev,
|
||||||
bcl_perph->param[BCL_PARAM_CURRENT].irq_num,
|
bcl_perph->param[BCL_PARAM_CURRENT].irq_num,
|
||||||
NULL, bcl_handle_ibat,
|
NULL, bcl_handle_ibat,
|
||||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||||
"bcl_ibat_interrupt",
|
"bcl_ibat_interrupt",
|
||||||
&bcl_perph->param[BCL_PARAM_CURRENT]);
|
&bcl_perph->param[BCL_PARAM_CURRENT]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&spmi->dev, "Error requesting IBAT irq. err:%d", ret);
|
dev_err(&pdev->dev, "Error requesting IBAT irq. err:%d", ret);
|
||||||
mutex_unlock(
|
mutex_unlock(
|
||||||
&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
||||||
goto bcl_probe_exit;
|
goto bcl_probe_exit;
|
||||||
|
@ -1085,7 +1078,7 @@ static int bcl_probe(struct spmi_device *spmi)
|
||||||
disable_irq_nosync(bcl_perph->param[BCL_PARAM_CURRENT].irq_num);
|
disable_irq_nosync(bcl_perph->param[BCL_PARAM_CURRENT].irq_num);
|
||||||
mutex_unlock(&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
mutex_unlock(&bcl_perph->param[BCL_PARAM_CURRENT].state_trans_lock);
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, bcl_perph);
|
dev_set_drvdata(&pdev->dev, bcl_perph);
|
||||||
ret = bcl_write_register(BCL_MONITOR_EN, BIT(7));
|
ret = bcl_write_register(BCL_MONITOR_EN, BIT(7));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Error accessing BCL peripheral. err:%d\n", ret);
|
pr_err("Error accessing BCL peripheral. err:%d\n", ret);
|
||||||
|
@ -1099,7 +1092,7 @@ bcl_probe_exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcl_remove(struct spmi_device *spmi)
|
static int bcl_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret = 0, i = 0;
|
int ret = 0, i = 0;
|
||||||
|
|
||||||
|
@ -1126,25 +1119,25 @@ static struct of_device_id bcl_match[] = {
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver bcl_driver = {
|
static struct platform_driver bcl_driver = {
|
||||||
.probe = bcl_probe,
|
.probe = bcl_probe,
|
||||||
.remove = bcl_remove,
|
.remove = bcl_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = BCL_DRIVER_NAME,
|
.name = BCL_DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = bcl_match,
|
.of_match_table = bcl_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init bcl_perph_init(void)
|
static int __init bcl_perph_init(void)
|
||||||
{
|
{
|
||||||
pr_info("BCL Initialized\n");
|
pr_info("BCL Initialized\n");
|
||||||
return spmi_driver_register(&bcl_driver);
|
return platform_driver_register(&bcl_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit bcl_perph_exit(void)
|
static void __exit bcl_perph_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&bcl_driver);
|
platform_driver_unregister(&bcl_driver);
|
||||||
}
|
}
|
||||||
fs_initcall(bcl_perph_init);
|
fs_initcall(bcl_perph_init);
|
||||||
module_exit(bcl_perph_exit);
|
module_exit(bcl_perph_exit);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
@ -396,7 +398,8 @@ static void fg_relax(struct fg_wakeup_source *source)
|
||||||
#define THERMAL_COEFF_N_BYTES 6
|
#define THERMAL_COEFF_N_BYTES 6
|
||||||
struct fg_chip {
|
struct fg_chip {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
u8 pmic_subtype;
|
u8 pmic_subtype;
|
||||||
u8 pmic_revision[4];
|
u8 pmic_revision[4];
|
||||||
u8 revision[4];
|
u8 revision[4];
|
||||||
|
@ -591,19 +594,19 @@ static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
|
||||||
static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
struct platform_device *pdev = chip->pdev;
|
||||||
char str[DEBUG_PRINT_BUFFER_SIZE];
|
char str[DEBUG_PRINT_BUFFER_SIZE];
|
||||||
|
|
||||||
if ((addr & 0xff00) == 0) {
|
if ((addr & 0xff00) == 0) {
|
||||||
pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
||||||
addr, spmi->sid, rc);
|
addr, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, len);
|
rc = regmap_bulk_write(chip->regmap, addr, val, len);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
|
||||||
addr, spmi->sid, rc);
|
addr, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,7 +614,8 @@ static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
||||||
str[0] = '\0';
|
str[0] = '\0';
|
||||||
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
|
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
|
||||||
pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
|
pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
|
||||||
addr, spmi->sid, len, str);
|
addr, to_spmi_device(pdev->dev.parent)->usid, len,
|
||||||
|
str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -620,19 +624,19 @@ static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
||||||
static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
struct platform_device *pdev = chip->pdev;
|
||||||
char str[DEBUG_PRINT_BUFFER_SIZE];
|
char str[DEBUG_PRINT_BUFFER_SIZE];
|
||||||
|
|
||||||
if ((addr & 0xff00) == 0) {
|
if ((addr & 0xff00) == 0) {
|
||||||
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
||||||
addr, spmi->sid, rc);
|
addr, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, len);
|
rc = regmap_bulk_read(chip->regmap, addr, val, len);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
|
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
|
||||||
spmi->sid, rc);
|
to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +644,8 @@ static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
|
||||||
str[0] = '\0';
|
str[0] = '\0';
|
||||||
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
|
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
|
||||||
pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
|
pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
|
||||||
addr, spmi->sid, len, str);
|
addr, to_spmi_device(pdev->dev.parent)->usid, len,
|
||||||
|
str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -650,21 +655,8 @@ static int fg_masked_write(struct fg_chip *chip, u16 addr,
|
||||||
u8 mask, u8 val, int len)
|
u8 mask, u8 val, int len)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg;
|
|
||||||
|
|
||||||
rc = fg_read(chip, ®, addr, len);
|
rc = regmap_update_bits(chip->regmap, addr, mask, val);
|
||||||
if (rc) {
|
|
||||||
pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
pr_debug("addr = 0x%x read 0x%x\n", addr, reg);
|
|
||||||
|
|
||||||
reg &= ~mask;
|
|
||||||
reg |= val & mask;
|
|
||||||
|
|
||||||
pr_debug("Writing 0x%x\n", reg);
|
|
||||||
|
|
||||||
rc = fg_write(chip, ®, addr, len);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
|
pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -4563,7 +4555,7 @@ static int fg_batt_profile_init(struct fg_chip *chip)
|
||||||
{
|
{
|
||||||
int rc = 0, ret;
|
int rc = 0, ret;
|
||||||
int len;
|
int len;
|
||||||
struct device_node *node = chip->spmi->dev.of_node;
|
struct device_node *node = chip->pdev->dev.of_node;
|
||||||
struct device_node *batt_node, *profile_node;
|
struct device_node *batt_node, *profile_node;
|
||||||
const char *data, *batt_type_str;
|
const char *data, *batt_type_str;
|
||||||
bool tried_again = false, vbat_in_range, profiles_same;
|
bool tried_again = false, vbat_in_range, profiles_same;
|
||||||
|
@ -4642,7 +4634,7 @@ wait:
|
||||||
* Only configure from profile if fg-cc-cv-threshold-mv is not
|
* Only configure from profile if fg-cc-cv-threshold-mv is not
|
||||||
* defined in the charger device node.
|
* defined in the charger device node.
|
||||||
*/
|
*/
|
||||||
if (!of_find_property(chip->spmi->dev.of_node,
|
if (!of_find_property(chip->pdev->dev.of_node,
|
||||||
"qcom,fg-cc-cv-threshold-mv", NULL)) {
|
"qcom,fg-cc-cv-threshold-mv", NULL)) {
|
||||||
of_property_read_u32(profile_node,
|
of_property_read_u32(profile_node,
|
||||||
"qcom,fg-cc-cv-threshold-mv",
|
"qcom,fg-cc-cv-threshold-mv",
|
||||||
|
@ -4757,7 +4749,7 @@ wait:
|
||||||
* Only configure from profile if thermal-coefficients is not
|
* Only configure from profile if thermal-coefficients is not
|
||||||
* defined in the FG device node.
|
* defined in the FG device node.
|
||||||
*/
|
*/
|
||||||
if (!of_find_property(chip->spmi->dev.of_node,
|
if (!of_find_property(chip->pdev->dev.of_node,
|
||||||
"qcom,thermal-coefficients", NULL)) {
|
"qcom,thermal-coefficients", NULL)) {
|
||||||
data = of_get_property(profile_node,
|
data = of_get_property(profile_node,
|
||||||
"qcom,thermal-coefficients", &len);
|
"qcom,thermal-coefficients", &len);
|
||||||
|
@ -5048,7 +5040,7 @@ do { \
|
||||||
if (retval) \
|
if (retval) \
|
||||||
break; \
|
break; \
|
||||||
\
|
\
|
||||||
retval = of_property_read_u32(chip->spmi->dev.of_node, \
|
retval = of_property_read_u32(chip->pdev->dev.of_node, \
|
||||||
"qcom," qpnp_dt_property, \
|
"qcom," qpnp_dt_property, \
|
||||||
&settings[type].value); \
|
&settings[type].value); \
|
||||||
\
|
\
|
||||||
|
@ -5064,7 +5056,7 @@ do { \
|
||||||
if (retval) \
|
if (retval) \
|
||||||
break; \
|
break; \
|
||||||
\
|
\
|
||||||
retval = of_property_read_u32(chip->spmi->dev.of_node, \
|
retval = of_property_read_u32(chip->pdev->dev.of_node, \
|
||||||
"qcom," qpnp_dt_property, \
|
"qcom," qpnp_dt_property, \
|
||||||
&store); \
|
&store); \
|
||||||
\
|
\
|
||||||
|
@ -5082,7 +5074,7 @@ static int fg_of_init(struct fg_chip *chip)
|
||||||
{
|
{
|
||||||
int rc = 0, sense_type, len = 0;
|
int rc = 0, sense_type, len = 0;
|
||||||
const char *data;
|
const char *data;
|
||||||
struct device_node *node = chip->spmi->dev.of_node;
|
struct device_node *node = chip->pdev->dev.of_node;
|
||||||
u32 temp[2] = {0};
|
u32 temp[2] = {0};
|
||||||
|
|
||||||
OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
|
OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
|
||||||
|
@ -5126,7 +5118,7 @@ static int fg_of_init(struct fg_chip *chip)
|
||||||
OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-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_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1);
|
||||||
OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
|
OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
|
||||||
data = of_get_property(chip->spmi->dev.of_node,
|
data = of_get_property(chip->pdev->dev.of_node,
|
||||||
"qcom,thermal-coefficients", &len);
|
"qcom,thermal-coefficients", &len);
|
||||||
if (data && len == THERMAL_COEFF_N_BYTES) {
|
if (data && len == THERMAL_COEFF_N_BYTES) {
|
||||||
memcpy(chip->thermal_coefficients, data, len);
|
memcpy(chip->thermal_coefficients, data, len);
|
||||||
|
@ -5159,31 +5151,30 @@ static int fg_of_init(struct fg_chip *chip)
|
||||||
DEFAULT_EVALUATION_CURRENT_MA);
|
DEFAULT_EVALUATION_CURRENT_MA);
|
||||||
OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
|
OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
|
||||||
"fg-cc-cv-threshold-mv", rc, 0);
|
"fg-cc-cv-threshold-mv", rc, 0);
|
||||||
if (of_property_read_bool(chip->spmi->dev.of_node,
|
if (of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
"qcom,capacity-learning-on"))
|
"qcom,capacity-learning-on"))
|
||||||
chip->batt_aging_mode = FG_AGING_CC;
|
chip->batt_aging_mode = FG_AGING_CC;
|
||||||
else if (of_property_read_bool(chip->spmi->dev.of_node,
|
else if (of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
"qcom,capacity-estimation-on"))
|
"qcom,capacity-estimation-on"))
|
||||||
chip->batt_aging_mode = FG_AGING_ESR;
|
chip->batt_aging_mode = FG_AGING_ESR;
|
||||||
else
|
else
|
||||||
chip->batt_aging_mode = FG_AGING_NONE;
|
chip->batt_aging_mode = FG_AGING_NONE;
|
||||||
if (chip->batt_aging_mode == FG_AGING_CC) {
|
if (chip->batt_aging_mode == FG_AGING_CC) {
|
||||||
chip->learning_data.feedback_on = of_property_read_bool(
|
chip->learning_data.feedback_on
|
||||||
chip->spmi->dev.of_node,
|
= of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
"qcom,capacity-learning-feedback");
|
"qcom,capacity-learning-feedback");
|
||||||
}
|
}
|
||||||
if (fg_debug_mask & FG_AGING)
|
if (fg_debug_mask & FG_AGING)
|
||||||
pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
|
pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
|
||||||
|
|
||||||
/* Get the use-otp-profile property */
|
/* Get the use-otp-profile property */
|
||||||
chip->use_otp_profile = of_property_read_bool(
|
chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
chip->spmi->dev.of_node,
|
|
||||||
"qcom,use-otp-profile");
|
"qcom,use-otp-profile");
|
||||||
chip->hold_soc_while_full = of_property_read_bool(
|
chip->hold_soc_while_full
|
||||||
chip->spmi->dev.of_node,
|
= of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
"qcom,hold-soc-while-full");
|
"qcom,hold-soc-while-full");
|
||||||
|
|
||||||
sense_type = of_property_read_bool(chip->spmi->dev.of_node,
|
sense_type = of_property_read_bool(chip->pdev->dev.of_node,
|
||||||
"qcom,ext-sense-type");
|
"qcom,ext-sense-type");
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
if (fg_sense_type < 0)
|
if (fg_sense_type < 0)
|
||||||
|
@ -5218,32 +5209,32 @@ static int fg_of_init(struct fg_chip *chip)
|
||||||
static int fg_init_irqs(struct fg_chip *chip)
|
static int fg_init_irqs(struct fg_chip *chip)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
u8 subtype;
|
u8 subtype;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
struct platform_device *pdev = chip->pdev;
|
||||||
|
|
||||||
spmi_for_each_container_dev(spmi_resource, spmi) {
|
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
|
||||||
if (!spmi_resource) {
|
pr_err("no child nodes\n");
|
||||||
pr_err("fg: spmi resource absent\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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
if ((base == chip->vbat_adc_addr) ||
|
||||||
IORESOURCE_MEM, 0);
|
(base == chip->ibat_adc_addr) ||
|
||||||
if (!(resource && resource->start)) {
|
(base == chip->tp_rev_addr))
|
||||||
pr_err("node %s IO resource absent!\n",
|
|
||||||
spmi->dev.of_node->full_name);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((resource->start == chip->vbat_adc_addr) ||
|
|
||||||
(resource->start == chip->ibat_adc_addr) ||
|
|
||||||
(resource->start == chip->tp_rev_addr))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
rc = fg_read(chip, &subtype,
|
rc = fg_read(chip, &subtype,
|
||||||
resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
|
base + REG_OFFSET_PERP_SUBTYPE, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Peripheral subtype read failed rc=%d\n", rc);
|
pr_err("Peripheral subtype read failed rc=%d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -5251,26 +5242,26 @@ static int fg_init_irqs(struct fg_chip *chip)
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case FG_SOC:
|
case FG_SOC:
|
||||||
chip->soc_irq[FULL_SOC].irq = spmi_get_irq_byname(
|
chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child,
|
||||||
chip->spmi, spmi_resource, "full-soc");
|
"full-soc");
|
||||||
if (chip->soc_irq[FULL_SOC].irq < 0) {
|
if (chip->soc_irq[FULL_SOC].irq < 0) {
|
||||||
pr_err("Unable to get full-soc irq\n");
|
pr_err("Unable to get full-soc irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
chip->soc_irq[EMPTY_SOC].irq = spmi_get_irq_byname(
|
chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child,
|
||||||
chip->spmi, spmi_resource, "empty-soc");
|
"empty-soc");
|
||||||
if (chip->soc_irq[EMPTY_SOC].irq < 0) {
|
if (chip->soc_irq[EMPTY_SOC].irq < 0) {
|
||||||
pr_err("Unable to get low-soc irq\n");
|
pr_err("Unable to get low-soc irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
chip->soc_irq[DELTA_SOC].irq = spmi_get_irq_byname(
|
chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child,
|
||||||
chip->spmi, spmi_resource, "delta-soc");
|
"delta-soc");
|
||||||
if (chip->soc_irq[DELTA_SOC].irq < 0) {
|
if (chip->soc_irq[DELTA_SOC].irq < 0) {
|
||||||
pr_err("Unable to get delta-soc irq\n");
|
pr_err("Unable to get delta-soc irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
chip->soc_irq[FIRST_EST_DONE].irq = spmi_get_irq_byname(
|
chip->soc_irq[FIRST_EST_DONE].irq
|
||||||
chip->spmi, spmi_resource, "first-est-done");
|
= of_irq_get_byname(child, "first-est-done");
|
||||||
if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
|
if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
|
||||||
pr_err("Unable to get first-est-done irq\n");
|
pr_err("Unable to get first-est-done irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -5319,8 +5310,8 @@ static int fg_init_irqs(struct fg_chip *chip)
|
||||||
enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
|
enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
|
||||||
break;
|
break;
|
||||||
case FG_MEMIF:
|
case FG_MEMIF:
|
||||||
chip->mem_irq[FG_MEM_AVAIL].irq = spmi_get_irq_byname(
|
chip->mem_irq[FG_MEM_AVAIL].irq
|
||||||
chip->spmi, spmi_resource, "mem-avail");
|
= of_irq_get_byname(child, "mem-avail");
|
||||||
if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
|
if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
|
||||||
pr_err("Unable to get mem-avail irq\n");
|
pr_err("Unable to get mem-avail irq\n");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -5338,9 +5329,8 @@ static int fg_init_irqs(struct fg_chip *chip)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FG_BATT:
|
case FG_BATT:
|
||||||
chip->batt_irq[BATT_MISSING].irq = spmi_get_irq_byname(
|
chip->batt_irq[BATT_MISSING].irq
|
||||||
chip->spmi, spmi_resource,
|
= of_irq_get_byname(child, "batt-missing");
|
||||||
"batt-missing");
|
|
||||||
if (chip->batt_irq[BATT_MISSING].irq < 0) {
|
if (chip->batt_irq[BATT_MISSING].irq < 0) {
|
||||||
pr_err("Unable to get batt-missing irq\n");
|
pr_err("Unable to get batt-missing irq\n");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
@ -5359,9 +5349,8 @@ static int fg_init_irqs(struct fg_chip *chip)
|
||||||
chip->batt_irq[BATT_MISSING].irq, rc);
|
chip->batt_irq[BATT_MISSING].irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
chip->batt_irq[VBATT_LOW].irq = spmi_get_irq_byname(
|
chip->batt_irq[VBATT_LOW].irq
|
||||||
chip->spmi, spmi_resource,
|
= of_irq_get_byname(child, "vbatt-low");
|
||||||
"vbatt-low");
|
|
||||||
if (chip->batt_irq[VBATT_LOW].irq < 0) {
|
if (chip->batt_irq[VBATT_LOW].irq < 0) {
|
||||||
pr_err("Unable to get vbatt-low irq\n");
|
pr_err("Unable to get vbatt-low irq\n");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
@ -5427,12 +5416,12 @@ static void fg_cleanup(struct fg_chip *chip)
|
||||||
wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
|
wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fg_remove(struct spmi_device *spmi)
|
static int fg_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct fg_chip *chip = dev_get_drvdata(&spmi->dev);
|
struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
fg_cleanup(chip);
|
fg_cleanup(chip);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6176,7 +6165,7 @@ static int fg_detect_pmic_type(struct fg_chip *chip)
|
||||||
struct pmic_revid_data *pmic_rev_id;
|
struct pmic_revid_data *pmic_rev_id;
|
||||||
struct device_node *revid_dev_node;
|
struct device_node *revid_dev_node;
|
||||||
|
|
||||||
revid_dev_node = of_parse_phandle(chip->spmi->dev.of_node,
|
revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
|
||||||
"qcom,pmic-revid", 0);
|
"qcom,pmic-revid", 0);
|
||||||
if (!revid_dev_node) {
|
if (!revid_dev_node) {
|
||||||
pr_err("Missing qcom,pmic-revid property - driver failed\n");
|
pr_err("Missing qcom,pmic-revid property - driver failed\n");
|
||||||
|
@ -6309,21 +6298,21 @@ done:
|
||||||
fg_cleanup(chip);
|
fg_cleanup(chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fg_probe(struct spmi_device *spmi)
|
static int fg_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &(spmi->dev);
|
struct device *dev = &(pdev->dev);
|
||||||
struct fg_chip *chip;
|
struct fg_chip *chip;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
u8 subtype, reg;
|
u8 subtype, reg;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (!spmi) {
|
if (!pdev) {
|
||||||
pr_err("no valid spmi pointer\n");
|
pr_err("no valid spmi pointer\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spmi->dev.of_node) {
|
if (!pdev->dev.of_node) {
|
||||||
pr_err("device node missing\n");
|
pr_err("device node missing\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -6333,9 +6322,14 @@ static int fg_probe(struct spmi_device *spmi)
|
||||||
pr_err("Can't allocate fg_chip\n");
|
pr_err("Can't allocate fg_chip\n");
|
||||||
return -ENOMEM;
|
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->spmi = spmi;
|
chip->pdev = pdev;
|
||||||
chip->dev = &(spmi->dev);
|
chip->dev = &(pdev->dev);
|
||||||
|
|
||||||
wakeup_source_init(&chip->empty_check_wakeup_source.source,
|
wakeup_source_init(&chip->empty_check_wakeup_source.source,
|
||||||
"qpnp_fg_empty_check");
|
"qpnp_fg_empty_check");
|
||||||
|
@ -6383,40 +6377,36 @@ static int fg_probe(struct spmi_device *spmi)
|
||||||
complete_all(&chip->sram_access_revoked);
|
complete_all(&chip->sram_access_revoked);
|
||||||
init_completion(&chip->batt_id_avail);
|
init_completion(&chip->batt_id_avail);
|
||||||
init_completion(&chip->first_soc_done);
|
init_completion(&chip->first_soc_done);
|
||||||
dev_set_drvdata(&spmi->dev, chip);
|
dev_set_drvdata(&pdev->dev, chip);
|
||||||
|
|
||||||
spmi_for_each_container_dev(spmi_resource, spmi) {
|
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
|
||||||
if (!spmi_resource) {
|
pr_err("no child nodes\n");
|
||||||
pr_err("qpnp_chg: spmi resource absent\n");
|
rc = -ENXIO;
|
||||||
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;
|
goto of_init_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
if (strcmp("qcom,fg-adc-vbat", child->name) == 0) {
|
||||||
IORESOURCE_MEM, 0);
|
chip->vbat_adc_addr = base;
|
||||||
if (!(resource && resource->start)) {
|
|
||||||
pr_err("node %s IO resource absent!\n",
|
|
||||||
spmi->dev.of_node->full_name);
|
|
||||||
rc = -ENXIO;
|
|
||||||
goto of_init_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("qcom,fg-adc-vbat",
|
|
||||||
spmi_resource->of_node->name) == 0) {
|
|
||||||
chip->vbat_adc_addr = resource->start;
|
|
||||||
continue;
|
continue;
|
||||||
} else if (strcmp("qcom,fg-adc-ibat",
|
} else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) {
|
||||||
spmi_resource->of_node->name) == 0) {
|
chip->ibat_adc_addr = base;
|
||||||
chip->ibat_adc_addr = resource->start;
|
|
||||||
continue;
|
continue;
|
||||||
} else if (strcmp("qcom,revid-tp-rev",
|
} else if (strcmp("qcom,revid-tp-rev", child->name) == 0) {
|
||||||
spmi_resource->of_node->name) == 0) {
|
chip->tp_rev_addr = base;
|
||||||
chip->tp_rev_addr = resource->start;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = fg_read(chip, &subtype,
|
rc = fg_read(chip, &subtype,
|
||||||
resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
|
base + REG_OFFSET_PERP_SUBTYPE, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Peripheral subtype read failed rc=%d\n", rc);
|
pr_err("Peripheral subtype read failed rc=%d\n", rc);
|
||||||
goto of_init_fail;
|
goto of_init_fail;
|
||||||
|
@ -6424,13 +6414,13 @@ static int fg_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case FG_SOC:
|
case FG_SOC:
|
||||||
chip->soc_base = resource->start;
|
chip->soc_base = base;
|
||||||
break;
|
break;
|
||||||
case FG_MEMIF:
|
case FG_MEMIF:
|
||||||
chip->mem_base = resource->start;
|
chip->mem_base = base;
|
||||||
break;
|
break;
|
||||||
case FG_BATT:
|
case FG_BATT:
|
||||||
chip->batt_base = resource->start;
|
chip->batt_base = base;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("Invalid peripheral subtype=0x%x\n", subtype);
|
pr_err("Invalid peripheral subtype=0x%x\n", subtype);
|
||||||
|
@ -6693,11 +6683,11 @@ static struct kernel_param_ops fg_restart_ops = {
|
||||||
|
|
||||||
module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
|
module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
|
||||||
|
|
||||||
static struct spmi_driver fg_driver = {
|
static struct platform_driver fg_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_FG_DEV_NAME,
|
.name = QPNP_FG_DEV_NAME,
|
||||||
.of_match_table = fg_match_table,
|
.of_match_table = fg_match_table,
|
||||||
.pm = &qpnp_fg_pm_ops,
|
.pm = &qpnp_fg_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = fg_probe,
|
.probe = fg_probe,
|
||||||
.remove = fg_remove,
|
.remove = fg_remove,
|
||||||
|
@ -6705,12 +6695,12 @@ static struct spmi_driver fg_driver = {
|
||||||
|
|
||||||
static int __init fg_init(void)
|
static int __init fg_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&fg_driver);
|
return platform_driver_register(&fg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit fg_exit(void)
|
static void __exit fg_exit(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_unregister(&fg_driver);
|
return platform_driver_unregister(&fg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(fg_init);
|
module_init(fg_init);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*/
|
*/
|
||||||
#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__
|
#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__
|
||||||
|
|
||||||
#include <linux/spmi.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
@ -23,11 +23,13 @@
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/of_regulator.h>
|
#include <linux/regulator/of_regulator.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
@ -94,7 +96,8 @@ struct smbchg_version_tables {
|
||||||
|
|
||||||
struct smbchg_chip {
|
struct smbchg_chip {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
int schg_version;
|
int schg_version;
|
||||||
|
|
||||||
/* peripheral register address bases */
|
/* peripheral register address bases */
|
||||||
|
@ -493,55 +496,24 @@ static int smbchg_read(struct smbchg_chip *chip, u8 *val,
|
||||||
u16 addr, int count)
|
u16 addr, int count)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
struct platform_device *pdev = chip->pdev;
|
||||||
|
|
||||||
if (addr == 0) {
|
if (addr == 0) {
|
||||||
dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
|
dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
|
||||||
addr, spmi->sid, rc);
|
addr, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, count);
|
rc = regmap_bulk_read(chip->regmap, addr, val, count);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
|
dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
|
||||||
addr, spmi->sid, rc);
|
addr, to_spmi_device(pdev->dev.parent)->usid,
|
||||||
|
rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes an arbitrary number of bytes to a specified register
|
|
||||||
*
|
|
||||||
* 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_write(struct smbchg_chip *chip, u8 *val,
|
|
||||||
u16 addr, int count)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
struct spmi_device *spmi = chip->spmi;
|
|
||||||
|
|
||||||
if (addr == 0) {
|
|
||||||
dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
|
|
||||||
addr, spmi->sid, rc);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, count);
|
|
||||||
if (rc) {
|
|
||||||
dev_err(chip->dev, "write failed addr=0x%02x sid=0x%02x rc=%d\n",
|
|
||||||
addr, spmi->sid, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writes a register to the specified by the base and limited by the bit mask
|
* Writes a register to the specified by the base and limited by the bit mask
|
||||||
*
|
*
|
||||||
|
@ -556,21 +528,8 @@ static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
|
||||||
u8 val)
|
u8 val)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg;
|
|
||||||
|
|
||||||
rc = smbchg_read(chip, ®, base, 1);
|
rc = regmap_update_bits(chip->regmap, base, mask, val);
|
||||||
if (rc) {
|
|
||||||
dev_err(chip->dev, "spmi read failed: addr=%03X, rc=%d\n",
|
|
||||||
base, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg &= ~mask;
|
|
||||||
reg |= val & mask;
|
|
||||||
|
|
||||||
pr_smb(PR_REGISTER, "addr = 0x%x writing 0x%x\n", base, reg);
|
|
||||||
|
|
||||||
rc = smbchg_write(chip, ®, base, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
|
dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
|
||||||
base, rc);
|
base, rc);
|
||||||
|
@ -3428,7 +3387,7 @@ 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;
|
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 *batt_node, *profile_node;
|
||||||
struct device_node *node = chip->spmi->dev.of_node;
|
struct device_node *node = chip->pdev->dev.of_node;
|
||||||
union power_supply_propval prop = {0,};
|
union power_supply_propval prop = {0,};
|
||||||
|
|
||||||
rc = chip->bms_psy->get_property(chip->bms_psy,
|
rc = chip->bms_psy->get_property(chip->bms_psy,
|
||||||
|
@ -3508,7 +3467,7 @@ static int smbchg_config_chg_battery_type(struct smbchg_chip *chip)
|
||||||
* Only configure from profile if fastchg-ma is not defined in the
|
* Only configure from profile if fastchg-ma is not defined in the
|
||||||
* charger device node.
|
* charger device node.
|
||||||
*/
|
*/
|
||||||
if (!of_find_property(chip->spmi->dev.of_node,
|
if (!of_find_property(chip->pdev->dev.of_node,
|
||||||
"qcom,fastchg-current-ma", NULL)) {
|
"qcom,fastchg-current-ma", NULL)) {
|
||||||
rc = of_property_read_u32(profile_node,
|
rc = of_property_read_u32(profile_node,
|
||||||
"qcom,fastchg-current-ma", &fastchg_ma);
|
"qcom,fastchg-current-ma", &fastchg_ma);
|
||||||
|
@ -7044,7 +7003,7 @@ do { \
|
||||||
if (optional) \
|
if (optional) \
|
||||||
prop = -EINVAL; \
|
prop = -EINVAL; \
|
||||||
\
|
\
|
||||||
retval = of_property_read_u32(chip->spmi->dev.of_node, \
|
retval = of_property_read_u32(chip->pdev->dev.of_node, \
|
||||||
"qcom," dt_property , \
|
"qcom," dt_property , \
|
||||||
&prop); \
|
&prop); \
|
||||||
\
|
\
|
||||||
|
@ -7213,11 +7172,9 @@ static int smb_parse_dt(struct smbchg_chip *chip)
|
||||||
/*
|
/*
|
||||||
* use the dt values if they exist, otherwise do not touch the params
|
* use the dt values if they exist, otherwise do not touch the params
|
||||||
*/
|
*/
|
||||||
of_property_read_u32(chip->spmi->dev.of_node,
|
of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent",
|
||||||
"qcom,parallel-main-chg-fcc-percent",
|
|
||||||
&smbchg_main_chg_fcc_percent);
|
&smbchg_main_chg_fcc_percent);
|
||||||
of_property_read_u32(chip->spmi->dev.of_node,
|
of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent",
|
||||||
"qcom,parallel-main-chg-icl-percent",
|
|
||||||
&smbchg_main_chg_icl_percent);
|
&smbchg_main_chg_icl_percent);
|
||||||
pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
|
pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
|
||||||
chip->parallel.min_current_thr_ma,
|
chip->parallel.min_current_thr_ma,
|
||||||
|
@ -7254,7 +7211,7 @@ static int smb_parse_dt(struct smbchg_chip *chip)
|
||||||
"qcom,skip-usb-suspend-for-fake-battery");
|
"qcom,skip-usb-suspend-for-fake-battery");
|
||||||
|
|
||||||
/* parse the battery missing detection pin source */
|
/* parse the battery missing detection pin source */
|
||||||
rc = of_property_read_string(chip->spmi->dev.of_node,
|
rc = of_property_read_string(chip->pdev->dev.of_node,
|
||||||
"qcom,bmd-pin-src", &bpd);
|
"qcom,bmd-pin-src", &bpd);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
/* Select BAT_THM as default BPD scheme */
|
/* Select BAT_THM as default BPD scheme */
|
||||||
|
@ -7360,50 +7317,52 @@ static int smb_parse_dt(struct smbchg_chip *chip)
|
||||||
#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54
|
#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54
|
||||||
#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55
|
#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55
|
||||||
#define SMBCHG_LITE_MISC_SUBTYPE 0x57
|
#define SMBCHG_LITE_MISC_SUBTYPE 0x57
|
||||||
#define REQUEST_IRQ(chip, resource, irq_num, irq_name, irq_handler, flags, rc)\
|
static int smbchg_request_irq(struct smbchg_chip *chip,
|
||||||
do { \
|
struct device_node *child,
|
||||||
irq_num = spmi_get_irq_byname(chip->spmi, \
|
int irq_num, char *irq_name,
|
||||||
resource, irq_name); \
|
irqreturn_t (irq_handler)(int irq, void *_chip),
|
||||||
if (irq_num < 0) { \
|
int flags)
|
||||||
dev_err(chip->dev, "Unable to get " irq_name " irq\n"); \
|
{
|
||||||
return -ENXIO; \
|
int rc;
|
||||||
} \
|
|
||||||
rc = devm_request_threaded_irq(chip->dev, \
|
irq_num = of_irq_get_byname(child, irq_name);
|
||||||
irq_num, NULL, irq_handler, flags, irq_name, \
|
if (irq_num < 0) {
|
||||||
chip); \
|
dev_err(chip->dev, "Unable to get %s irqn", irq_name);
|
||||||
if (rc < 0) { \
|
rc = -ENXIO;
|
||||||
dev_err(chip->dev, "Unable to request " irq_name " irq: %d\n",\
|
}
|
||||||
rc); \
|
rc = devm_request_threaded_irq(chip->dev,
|
||||||
return -ENXIO; \
|
irq_num, NULL, irq_handler, flags, irq_name,
|
||||||
} \
|
chip);
|
||||||
} while (0)
|
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)
|
static int smbchg_request_irqs(struct smbchg_chip *chip)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
u8 subtype;
|
u8 subtype;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
|
||||||
unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
|
unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
|
||||||
| IRQF_ONESHOT;
|
| IRQF_ONESHOT;
|
||||||
|
|
||||||
spmi_for_each_container_dev(spmi_resource, chip->spmi) {
|
if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
|
||||||
if (!spmi_resource) {
|
pr_err("no child nodes\n");
|
||||||
dev_err(chip->dev, "spmi resource absent\n");
|
return -ENXIO;
|
||||||
return rc;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
|
||||||
IORESOURCE_MEM, 0);
|
|
||||||
if (!(resource && resource->start)) {
|
|
||||||
dev_err(chip->dev, "node %s IO resource absent!\n",
|
|
||||||
spmi->dev.of_node->full_name);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = smbchg_read(chip, &subtype,
|
|
||||||
resource->start + SUBTYPE_REG, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
|
dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
|
@ -7413,37 +7372,66 @@ static int smbchg_request_irqs(struct smbchg_chip *chip)
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case SMBCHG_CHGR_SUBTYPE:
|
case SMBCHG_CHGR_SUBTYPE:
|
||||||
case SMBCHG_LITE_CHGR_SUBTYPE:
|
case SMBCHG_LITE_CHGR_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->chg_error_irq,
|
rc = smbchg_request_irq(chip, child,
|
||||||
"chg-error", chg_error_handler, flags, rc);
|
chip->chg_error_irq, "chg-error",
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->taper_irq,
|
chg_error_handler, flags);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child, chip->taper_irq,
|
||||||
"chg-taper-thr", taper_handler,
|
"chg-taper-thr", taper_handler,
|
||||||
(IRQF_TRIGGER_RISING | IRQF_ONESHOT), rc);
|
(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
disable_irq_nosync(chip->taper_irq);
|
disable_irq_nosync(chip->taper_irq);
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->chg_term_irq,
|
rc = smbchg_request_irq(chip, child, chip->chg_term_irq,
|
||||||
"chg-tcc-thr", chg_term_handler,
|
"chg-tcc-thr", chg_term_handler,
|
||||||
(IRQF_TRIGGER_RISING | IRQF_ONESHOT), rc);
|
(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->recharge_irq,
|
if (rc < 0)
|
||||||
"chg-rechg-thr", recharge_handler, flags, rc);
|
return rc;
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->fastchg_irq,
|
rc = smbchg_request_irq(chip, child, chip->recharge_irq,
|
||||||
"chg-p2f-thr", fastchg_handler, flags, rc);
|
"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_term_irq);
|
||||||
enable_irq_wake(chip->chg_error_irq);
|
enable_irq_wake(chip->chg_error_irq);
|
||||||
enable_irq_wake(chip->fastchg_irq);
|
enable_irq_wake(chip->fastchg_irq);
|
||||||
break;
|
break;
|
||||||
case SMBCHG_BAT_IF_SUBTYPE:
|
case SMBCHG_BAT_IF_SUBTYPE:
|
||||||
case SMBCHG_LITE_BAT_IF_SUBTYPE:
|
case SMBCHG_LITE_BAT_IF_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->batt_hot_irq,
|
rc = smbchg_request_irq(chip, child, chip->batt_hot_irq,
|
||||||
"batt-hot", batt_hot_handler, flags, rc);
|
"batt-hot", batt_hot_handler, flags);
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->batt_warm_irq,
|
if (rc < 0)
|
||||||
"batt-warm", batt_warm_handler, flags, rc);
|
return rc;
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->batt_cool_irq,
|
rc = smbchg_request_irq(chip, child,
|
||||||
"batt-cool", batt_cool_handler, flags, rc);
|
chip->batt_warm_irq,
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->batt_cold_irq,
|
"batt-warm", batt_warm_handler, flags);
|
||||||
"batt-cold", batt_cold_handler, flags, rc);
|
if (rc < 0)
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->batt_missing_irq,
|
return rc;
|
||||||
"batt-missing", batt_pres_handler, flags, rc);
|
rc = smbchg_request_irq(chip, child,
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->vbat_low_irq,
|
chip->batt_cool_irq,
|
||||||
"batt-low", vbat_low_handler, flags, rc);
|
"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_hot_irq);
|
||||||
enable_irq_wake(chip->batt_warm_irq);
|
enable_irq_wake(chip->batt_warm_irq);
|
||||||
enable_irq_wake(chip->batt_cool_irq);
|
enable_irq_wake(chip->batt_cool_irq);
|
||||||
|
@ -7453,33 +7441,49 @@ static int smbchg_request_irqs(struct smbchg_chip *chip)
|
||||||
break;
|
break;
|
||||||
case SMBCHG_USB_CHGPTH_SUBTYPE:
|
case SMBCHG_USB_CHGPTH_SUBTYPE:
|
||||||
case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
|
case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->usbin_uv_irq,
|
rc = smbchg_request_irq(chip, child,
|
||||||
|
chip->usbin_uv_irq,
|
||||||
"usbin-uv", usbin_uv_handler,
|
"usbin-uv", usbin_uv_handler,
|
||||||
flags | IRQF_EARLY_RESUME, rc);
|
flags | IRQF_EARLY_RESUME);
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->usbin_ov_irq,
|
if (rc < 0)
|
||||||
"usbin-ov", usbin_ov_handler, flags, rc);
|
return rc;
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->src_detect_irq,
|
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",
|
"usbin-src-det",
|
||||||
src_detect_handler, flags, rc);
|
src_detect_handler, flags);
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->aicl_done_irq,
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child,
|
||||||
|
chip->aicl_done_irq,
|
||||||
"aicl-done",
|
"aicl-done",
|
||||||
aicl_done_handler,
|
aicl_done_handler,
|
||||||
(IRQF_TRIGGER_RISING | IRQF_ONESHOT),
|
(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
|
||||||
rc);
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
if (chip->schg_version != QPNP_SCHG_LITE) {
|
if (chip->schg_version != QPNP_SCHG_LITE) {
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->otg_fail_irq, "otg-fail",
|
chip->otg_fail_irq, "otg-fail",
|
||||||
otg_fail_handler, flags, rc);
|
otg_fail_handler, flags);
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->otg_oc_irq, "otg-oc",
|
chip->otg_oc_irq, "otg-oc",
|
||||||
otg_oc_handler,
|
otg_oc_handler,
|
||||||
(IRQF_TRIGGER_RISING | IRQF_ONESHOT),
|
(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
|
||||||
rc);
|
if (rc < 0)
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->usbid_change_irq, "usbid-change",
|
chip->usbid_change_irq, "usbid-change",
|
||||||
usbid_change_handler,
|
usbid_change_handler,
|
||||||
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT),
|
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
|
||||||
rc);
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
enable_irq_wake(chip->otg_oc_irq);
|
enable_irq_wake(chip->otg_oc_irq);
|
||||||
enable_irq_wake(chip->usbid_change_irq);
|
enable_irq_wake(chip->usbid_change_irq);
|
||||||
enable_irq_wake(chip->otg_fail_irq);
|
enable_irq_wake(chip->otg_fail_irq);
|
||||||
|
@ -7494,38 +7498,50 @@ static int smbchg_request_irqs(struct smbchg_chip *chip)
|
||||||
break;
|
break;
|
||||||
case SMBCHG_DC_CHGPTH_SUBTYPE:
|
case SMBCHG_DC_CHGPTH_SUBTYPE:
|
||||||
case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
|
case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->dcin_uv_irq,
|
rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq,
|
||||||
"dcin-uv", dcin_uv_handler, flags, rc);
|
"dcin-uv", dcin_uv_handler, flags);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
enable_irq_wake(chip->dcin_uv_irq);
|
enable_irq_wake(chip->dcin_uv_irq);
|
||||||
break;
|
break;
|
||||||
case SMBCHG_MISC_SUBTYPE:
|
case SMBCHG_MISC_SUBTYPE:
|
||||||
case SMBCHG_LITE_MISC_SUBTYPE:
|
case SMBCHG_LITE_MISC_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->power_ok_irq,
|
rc = smbchg_request_irq(chip, child, chip->power_ok_irq,
|
||||||
"power-ok", power_ok_handler, flags, rc);
|
"power-ok", power_ok_handler, flags);
|
||||||
REQUEST_IRQ(chip, spmi_resource, chip->chg_hot_irq,
|
if (rc < 0)
|
||||||
"temp-shutdown", chg_hot_handler, flags, rc);
|
return rc;
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
rc = smbchg_request_irq(chip, child, chip->chg_hot_irq,
|
||||||
chip->wdog_timeout_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",
|
||||||
wdog_timeout_handler, flags, rc);
|
wdog_timeout_handler, flags);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
enable_irq_wake(chip->chg_hot_irq);
|
enable_irq_wake(chip->chg_hot_irq);
|
||||||
enable_irq_wake(chip->wdog_timeout_irq);
|
enable_irq_wake(chip->wdog_timeout_irq);
|
||||||
break;
|
break;
|
||||||
case SMBCHG_OTG_SUBTYPE:
|
case SMBCHG_OTG_SUBTYPE:
|
||||||
break;
|
break;
|
||||||
case SMBCHG_LITE_OTG_SUBTYPE:
|
case SMBCHG_LITE_OTG_SUBTYPE:
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->usbid_change_irq, "usbid-change",
|
chip->usbid_change_irq, "usbid-change",
|
||||||
usbid_change_handler,
|
usbid_change_handler,
|
||||||
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT),
|
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
|
||||||
rc);
|
if (rc < 0)
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->otg_oc_irq, "otg-oc",
|
chip->otg_oc_irq, "otg-oc",
|
||||||
otg_oc_handler,
|
otg_oc_handler,
|
||||||
(IRQF_TRIGGER_RISING | IRQF_ONESHOT), rc);
|
(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
|
||||||
REQUEST_IRQ(chip, spmi_resource,
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
rc = smbchg_request_irq(chip, child,
|
||||||
chip->otg_fail_irq, "otg-fail",
|
chip->otg_fail_irq, "otg-fail",
|
||||||
otg_fail_handler, flags, rc);
|
otg_fail_handler, flags);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
enable_irq_wake(chip->usbid_change_irq);
|
enable_irq_wake(chip->usbid_change_irq);
|
||||||
enable_irq_wake(chip->otg_oc_irq);
|
enable_irq_wake(chip->otg_oc_irq);
|
||||||
enable_irq_wake(chip->otg_fail_irq);
|
enable_irq_wake(chip->otg_fail_irq);
|
||||||
|
@ -7547,27 +7563,23 @@ do { \
|
||||||
static int smbchg_parse_peripherals(struct smbchg_chip *chip)
|
static int smbchg_parse_peripherals(struct smbchg_chip *chip)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
u8 subtype;
|
u8 subtype;
|
||||||
struct spmi_device *spmi = chip->spmi;
|
|
||||||
|
|
||||||
spmi_for_each_container_dev(spmi_resource, chip->spmi) {
|
if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
|
||||||
if (!spmi_resource) {
|
pr_err("no child nodes\n");
|
||||||
dev_err(chip->dev, "spmi resource absent\n");
|
return -ENXIO;
|
||||||
return rc;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
|
||||||
IORESOURCE_MEM, 0);
|
|
||||||
if (!(resource && resource->start)) {
|
|
||||||
dev_err(chip->dev, "node %s IO resource absent!\n",
|
|
||||||
spmi->dev.of_node->full_name);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = smbchg_read(chip, &subtype,
|
|
||||||
resource->start + SUBTYPE_REG, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
|
dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
|
@ -7577,27 +7589,27 @@ static int smbchg_parse_peripherals(struct smbchg_chip *chip)
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case SMBCHG_CHGR_SUBTYPE:
|
case SMBCHG_CHGR_SUBTYPE:
|
||||||
case SMBCHG_LITE_CHGR_SUBTYPE:
|
case SMBCHG_LITE_CHGR_SUBTYPE:
|
||||||
chip->chgr_base = resource->start;
|
chip->chgr_base = base;
|
||||||
break;
|
break;
|
||||||
case SMBCHG_BAT_IF_SUBTYPE:
|
case SMBCHG_BAT_IF_SUBTYPE:
|
||||||
case SMBCHG_LITE_BAT_IF_SUBTYPE:
|
case SMBCHG_LITE_BAT_IF_SUBTYPE:
|
||||||
chip->bat_if_base = resource->start;
|
chip->bat_if_base = base;
|
||||||
break;
|
break;
|
||||||
case SMBCHG_USB_CHGPTH_SUBTYPE:
|
case SMBCHG_USB_CHGPTH_SUBTYPE:
|
||||||
case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
|
case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
|
||||||
chip->usb_chgpth_base = resource->start;
|
chip->usb_chgpth_base = base;
|
||||||
break;
|
break;
|
||||||
case SMBCHG_DC_CHGPTH_SUBTYPE:
|
case SMBCHG_DC_CHGPTH_SUBTYPE:
|
||||||
case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
|
case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
|
||||||
chip->dc_chgpth_base = resource->start;
|
chip->dc_chgpth_base = base;
|
||||||
break;
|
break;
|
||||||
case SMBCHG_MISC_SUBTYPE:
|
case SMBCHG_MISC_SUBTYPE:
|
||||||
case SMBCHG_LITE_MISC_SUBTYPE:
|
case SMBCHG_LITE_MISC_SUBTYPE:
|
||||||
chip->misc_base = resource->start;
|
chip->misc_base = base;
|
||||||
break;
|
break;
|
||||||
case SMBCHG_OTG_SUBTYPE:
|
case SMBCHG_OTG_SUBTYPE:
|
||||||
case SMBCHG_LITE_OTG_SUBTYPE:
|
case SMBCHG_LITE_OTG_SUBTYPE:
|
||||||
chip->otg_base = resource->start;
|
chip->otg_base = base;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7680,7 +7692,7 @@ static int smbchg_check_chg_version(struct smbchg_chip *chip)
|
||||||
struct device_node *revid_dev_node;
|
struct device_node *revid_dev_node;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
revid_dev_node = of_parse_phandle(chip->spmi->dev.of_node,
|
revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
|
||||||
"qcom,pmic-revid", 0);
|
"qcom,pmic-revid", 0);
|
||||||
if (!revid_dev_node) {
|
if (!revid_dev_node) {
|
||||||
pr_err("Missing qcom,pmic-revid property - driver failed\n");
|
pr_err("Missing qcom,pmic-revid property - driver failed\n");
|
||||||
|
@ -7786,7 +7798,7 @@ static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smbchg_probe(struct spmi_device *spmi)
|
static int smbchg_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct smbchg_chip *chip;
|
struct smbchg_chip *chip;
|
||||||
|
@ -7800,9 +7812,9 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_bool(spmi->dev.of_node, "qcom,external-typec")) {
|
if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) {
|
||||||
/* read the type power supply name */
|
/* read the type power supply name */
|
||||||
rc = of_property_read_string(spmi->dev.of_node,
|
rc = of_property_read_string(pdev->dev.of_node,
|
||||||
"qcom,typec-psy-name", &typec_psy_name);
|
"qcom,typec-psy-name", &typec_psy_name);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("failed to get prop typec-psy-name rc=%d\n",
|
pr_err("failed to get prop typec-psy-name rc=%d\n",
|
||||||
|
@ -7818,91 +7830,97 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_find_property(spmi->dev.of_node, "qcom,dcin-vadc", NULL)) {
|
if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) {
|
||||||
vadc_dev = qpnp_get_vadc(&spmi->dev, "dcin");
|
vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin");
|
||||||
if (IS_ERR(vadc_dev)) {
|
if (IS_ERR(vadc_dev)) {
|
||||||
rc = PTR_ERR(vadc_dev);
|
rc = PTR_ERR(vadc_dev);
|
||||||
if (rc != -EPROBE_DEFER)
|
if (rc != -EPROBE_DEFER)
|
||||||
dev_err(&spmi->dev, "Couldn't get vadc rc=%d\n",
|
dev_err(&pdev->dev,
|
||||||
|
"Couldn't get vadc rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_find_property(spmi->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
|
if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
|
||||||
vchg_vadc_dev = qpnp_get_vadc(&spmi->dev, "vchg_sns");
|
vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns");
|
||||||
if (IS_ERR(vchg_vadc_dev)) {
|
if (IS_ERR(vchg_vadc_dev)) {
|
||||||
rc = PTR_ERR(vchg_vadc_dev);
|
rc = PTR_ERR(vchg_vadc_dev);
|
||||||
if (rc != -EPROBE_DEFER)
|
if (rc != -EPROBE_DEFER)
|
||||||
dev_err(&spmi->dev, "Couldn't get vadc 'vchg' rc=%d\n",
|
dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chip = devm_kzalloc(&spmi->dev, sizeof(*chip), GFP_KERNEL);
|
|
||||||
if (!chip) {
|
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory\n");
|
if (!chip)
|
||||||
return -ENOMEM;
|
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(&spmi->dev,
|
chip->fcc_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: fcc",
|
"SMBCHG: fcc",
|
||||||
VOTE_MIN, NUM_FCC_VOTER, 2000,
|
VOTE_MIN, NUM_FCC_VOTER, 2000,
|
||||||
set_fastchg_current_vote_cb);
|
set_fastchg_current_vote_cb);
|
||||||
if (IS_ERR(chip->fcc_votable))
|
if (IS_ERR(chip->fcc_votable))
|
||||||
return PTR_ERR(chip->fcc_votable);
|
return PTR_ERR(chip->fcc_votable);
|
||||||
|
|
||||||
chip->usb_icl_votable = create_votable(&spmi->dev,
|
chip->usb_icl_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: usb_icl",
|
"SMBCHG: usb_icl",
|
||||||
VOTE_MIN, NUM_ICL_VOTER, 3000,
|
VOTE_MIN, NUM_ICL_VOTER, 3000,
|
||||||
set_usb_current_limit_vote_cb);
|
set_usb_current_limit_vote_cb);
|
||||||
if (IS_ERR(chip->usb_icl_votable))
|
if (IS_ERR(chip->usb_icl_votable))
|
||||||
return PTR_ERR(chip->usb_icl_votable);
|
return PTR_ERR(chip->usb_icl_votable);
|
||||||
|
|
||||||
chip->dc_icl_votable = create_votable(&spmi->dev,
|
chip->dc_icl_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: dcl_icl",
|
"SMBCHG: dcl_icl",
|
||||||
VOTE_MIN, NUM_ICL_VOTER, 3000,
|
VOTE_MIN, NUM_ICL_VOTER, 3000,
|
||||||
set_dc_current_limit_vote_cb);
|
set_dc_current_limit_vote_cb);
|
||||||
if (IS_ERR(chip->dc_icl_votable))
|
if (IS_ERR(chip->dc_icl_votable))
|
||||||
return PTR_ERR(chip->dc_icl_votable);
|
return PTR_ERR(chip->dc_icl_votable);
|
||||||
|
|
||||||
chip->usb_suspend_votable = create_votable(&spmi->dev,
|
chip->usb_suspend_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: usb_suspend",
|
"SMBCHG: usb_suspend",
|
||||||
VOTE_SET_ANY, NUM_EN_VOTERS, 0,
|
VOTE_SET_ANY, NUM_EN_VOTERS, 0,
|
||||||
usb_suspend_vote_cb);
|
usb_suspend_vote_cb);
|
||||||
if (IS_ERR(chip->usb_suspend_votable))
|
if (IS_ERR(chip->usb_suspend_votable))
|
||||||
return PTR_ERR(chip->usb_suspend_votable);
|
return PTR_ERR(chip->usb_suspend_votable);
|
||||||
|
|
||||||
chip->dc_suspend_votable = create_votable(&spmi->dev,
|
chip->dc_suspend_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: dc_suspend",
|
"SMBCHG: dc_suspend",
|
||||||
VOTE_SET_ANY, NUM_EN_VOTERS, 0,
|
VOTE_SET_ANY, NUM_EN_VOTERS, 0,
|
||||||
dc_suspend_vote_cb);
|
dc_suspend_vote_cb);
|
||||||
if (IS_ERR(chip->dc_suspend_votable))
|
if (IS_ERR(chip->dc_suspend_votable))
|
||||||
return PTR_ERR(chip->dc_suspend_votable);
|
return PTR_ERR(chip->dc_suspend_votable);
|
||||||
|
|
||||||
chip->battchg_suspend_votable = create_votable(&spmi->dev,
|
chip->battchg_suspend_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: battchg_suspend",
|
"SMBCHG: battchg_suspend",
|
||||||
VOTE_SET_ANY, NUM_BATTCHG_EN_VOTERS, 0,
|
VOTE_SET_ANY, NUM_BATTCHG_EN_VOTERS, 0,
|
||||||
charging_suspend_vote_cb);
|
charging_suspend_vote_cb);
|
||||||
if (IS_ERR(chip->battchg_suspend_votable))
|
if (IS_ERR(chip->battchg_suspend_votable))
|
||||||
return PTR_ERR(chip->battchg_suspend_votable);
|
return PTR_ERR(chip->battchg_suspend_votable);
|
||||||
|
|
||||||
chip->hw_aicl_rerun_disable_votable = create_votable(&spmi->dev,
|
chip->hw_aicl_rerun_disable_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: hwaicl_disable",
|
"SMBCHG: hwaicl_disable",
|
||||||
VOTE_SET_ANY, NUM_HW_AICL_DISABLE_VOTERS, 0,
|
VOTE_SET_ANY, NUM_HW_AICL_DISABLE_VOTERS, 0,
|
||||||
smbchg_hw_aicl_rerun_disable_cb);
|
smbchg_hw_aicl_rerun_disable_cb);
|
||||||
if (IS_ERR(chip->hw_aicl_rerun_disable_votable))
|
if (IS_ERR(chip->hw_aicl_rerun_disable_votable))
|
||||||
return PTR_ERR(chip->hw_aicl_rerun_disable_votable);
|
return PTR_ERR(chip->hw_aicl_rerun_disable_votable);
|
||||||
|
|
||||||
chip->hw_aicl_rerun_enable_indirect_votable = create_votable(&spmi->dev,
|
chip->hw_aicl_rerun_enable_indirect_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: hwaicl_enable_indirect",
|
"SMBCHG: hwaicl_enable_indirect",
|
||||||
VOTE_SET_ANY, NUM_HW_AICL_RERUN_ENABLE_INDIRECT_VOTERS,
|
VOTE_SET_ANY, NUM_HW_AICL_RERUN_ENABLE_INDIRECT_VOTERS,
|
||||||
0, smbchg_hw_aicl_rerun_enable_indirect_cb);
|
0, smbchg_hw_aicl_rerun_enable_indirect_cb);
|
||||||
if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable))
|
if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable))
|
||||||
return PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
|
return PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
|
||||||
|
|
||||||
chip->aicl_deglitch_short_votable = create_votable(&spmi->dev,
|
chip->aicl_deglitch_short_votable = create_votable(&pdev->dev,
|
||||||
"SMBCHG: hwaicl_short_deglitch",
|
"SMBCHG: hwaicl_short_deglitch",
|
||||||
VOTE_SET_ANY, NUM_HW_SHORT_DEGLITCH_VOTERS, 0,
|
VOTE_SET_ANY, NUM_HW_SHORT_DEGLITCH_VOTERS, 0,
|
||||||
smbchg_aicl_deglitch_config_cb);
|
smbchg_aicl_deglitch_config_cb);
|
||||||
|
@ -7920,13 +7938,14 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
init_completion(&chip->usbin_uv_raised);
|
init_completion(&chip->usbin_uv_raised);
|
||||||
chip->vadc_dev = vadc_dev;
|
chip->vadc_dev = vadc_dev;
|
||||||
chip->vchg_vadc_dev = vchg_vadc_dev;
|
chip->vchg_vadc_dev = vchg_vadc_dev;
|
||||||
chip->spmi = spmi;
|
chip->pdev = pdev;
|
||||||
chip->dev = &spmi->dev;
|
chip->dev = &pdev->dev;
|
||||||
|
|
||||||
chip->usb_psy = usb_psy;
|
chip->usb_psy = usb_psy;
|
||||||
chip->typec_psy = typec_psy;
|
chip->typec_psy = typec_psy;
|
||||||
chip->fake_battery_soc = -EINVAL;
|
chip->fake_battery_soc = -EINVAL;
|
||||||
chip->usb_online = -EINVAL;
|
chip->usb_online = -EINVAL;
|
||||||
dev_set_drvdata(&spmi->dev, chip);
|
dev_set_drvdata(&pdev->dev, chip);
|
||||||
|
|
||||||
spin_lock_init(&chip->sec_access_lock);
|
spin_lock_init(&chip->sec_access_lock);
|
||||||
mutex_init(&chip->therm_lvl_lock);
|
mutex_init(&chip->therm_lvl_lock);
|
||||||
|
@ -7952,27 +7971,27 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = smb_parse_dt(chip);
|
rc = smb_parse_dt(chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "Unable to parse DT nodes: %d\n", rc);
|
dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = smbchg_regulator_init(chip);
|
rc = smbchg_regulator_init(chip);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Couldn't initialize regulator rc=%d\n", rc);
|
"Couldn't initialize regulator rc=%d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = smbchg_hw_init(chip);
|
rc = smbchg_hw_init(chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to intialize hardware rc = %d\n", rc);
|
"Unable to intialize hardware rc = %d\n", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = determine_initial_status(chip);
|
rc = determine_initial_status(chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to determine init status rc = %d\n", rc);
|
"Unable to determine init status rc = %d\n", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -7989,7 +8008,7 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = power_supply_register(chip->dev, &chip->batt_psy);
|
rc = power_supply_register(chip->dev, &chip->batt_psy);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to register batt_psy rc = %d\n", rc);
|
"Unable to register batt_psy rc = %d\n", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -8006,7 +8025,7 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
= ARRAY_SIZE(smbchg_dc_supplicants);
|
= ARRAY_SIZE(smbchg_dc_supplicants);
|
||||||
rc = power_supply_register(chip->dev, &chip->dc_psy);
|
rc = power_supply_register(chip->dev, &chip->dc_psy);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to register dc_psy rc = %d\n", rc);
|
"Unable to register dc_psy rc = %d\n", rc);
|
||||||
goto unregister_batt_psy;
|
goto unregister_batt_psy;
|
||||||
}
|
}
|
||||||
|
@ -8034,7 +8053,7 @@ static int smbchg_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = smbchg_request_irqs(chip);
|
rc = smbchg_request_irqs(chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "Unable to request irqs rc = %d\n", rc);
|
dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc);
|
||||||
goto unregister_led_class;
|
goto unregister_led_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8069,9 +8088,9 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smbchg_remove(struct spmi_device *spmi)
|
static int smbchg_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct smbchg_chip *chip = dev_get_drvdata(&spmi->dev);
|
struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
debugfs_remove_recursive(chip->debug_root);
|
debugfs_remove_recursive(chip->debug_root);
|
||||||
|
|
||||||
|
@ -8083,9 +8102,9 @@ static int smbchg_remove(struct spmi_device *spmi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smbchg_shutdown(struct spmi_device *spmi)
|
static void smbchg_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct smbchg_chip *chip = dev_get_drvdata(&spmi->dev);
|
struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||||
int i, rc;
|
int i, rc;
|
||||||
|
|
||||||
if (!(chip->wa_flags & SMBCHG_RESTART_WA))
|
if (!(chip->wa_flags & SMBCHG_RESTART_WA))
|
||||||
|
@ -8191,7 +8210,7 @@ static const struct dev_pm_ops smbchg_pm_ops = {
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(spmi, smbchg_id);
|
MODULE_DEVICE_TABLE(spmi, smbchg_id);
|
||||||
|
|
||||||
static struct spmi_driver smbchg_driver = {
|
static struct platform_driver smbchg_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qpnp-smbcharger",
|
.name = "qpnp-smbcharger",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
@ -8205,12 +8224,12 @@ static struct spmi_driver smbchg_driver = {
|
||||||
|
|
||||||
static int __init smbchg_init(void)
|
static int __init smbchg_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&smbchg_driver);
|
return platform_driver_register(&smbchg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit smbchg_exit(void)
|
static void __exit smbchg_exit(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_unregister(&smbchg_driver);
|
return platform_driver_unregister(&smbchg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(smbchg_init);
|
module_init(smbchg_init);
|
||||||
|
|
|
@ -310,8 +310,7 @@ config PWM_RCAR
|
||||||
will be called pwm-rcar.
|
will be called pwm-rcar.
|
||||||
|
|
||||||
config PWM_QPNP
|
config PWM_QPNP
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
tristate "Qualcomm QPNP LPG/PWM support"
|
tristate "Qualcomm QPNP LPG/PWM support"
|
||||||
help
|
help
|
||||||
This driver supports PWM/LPG devices in Qualcomm PMIC chips which
|
This driver supports PWM/LPG devices in Qualcomm PMIC chips which
|
||||||
|
|
|
@ -21,8 +21,11 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/radix-tree.h>
|
#include <linux/radix-tree.h>
|
||||||
#include <linux/qpnp/pwm.h>
|
#include <linux/qpnp/pwm.h>
|
||||||
|
|
||||||
|
@ -308,7 +311,8 @@ struct _qpnp_pwm_config {
|
||||||
|
|
||||||
/* Public facing structure */
|
/* Public facing structure */
|
||||||
struct qpnp_pwm_chip {
|
struct qpnp_pwm_chip {
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
struct pwm_chip chip;
|
struct pwm_chip chip;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
struct _qpnp_pwm_config pwm_config;
|
struct _qpnp_pwm_config pwm_config;
|
||||||
|
@ -429,8 +433,7 @@ static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 addr,
|
||||||
{
|
{
|
||||||
qpnp_lpg_save(reg, mask, value);
|
qpnp_lpg_save(reg, mask, value);
|
||||||
|
|
||||||
return spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
return regmap_bulk_write(chip->regmap, addr, reg, size);
|
||||||
chip->spmi_dev->sid, addr, reg, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -617,10 +620,10 @@ static int qpnp_lpg_change_table(struct qpnp_pwm_chip *chip,
|
||||||
for (i = 0; i < list_len; i += burst_size) {
|
for (i = 0; i < list_len; i += burst_size) {
|
||||||
if (i + burst_size >= list_len)
|
if (i + burst_size >= list_len)
|
||||||
burst_size = list_len - i;
|
burst_size = list_len - i;
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_bulk_write(chip->regmap,
|
||||||
chip->spmi_dev->sid,
|
chip->lpg_config.lut_base_addr + offset + i,
|
||||||
chip->lpg_config.lut_base_addr + offset + i,
|
lut->duty_pct_list + i,
|
||||||
lut->duty_pct_list + i, burst_size);
|
burst_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -702,10 +705,10 @@ static int qpnp_lpg_save_pwm_value(struct qpnp_pwm_chip *chip)
|
||||||
if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE ||
|
if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE ||
|
||||||
chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) {
|
chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) {
|
||||||
value = QPNP_PWM_SYNC_VALUE & QPNP_PWM_SYNC_MASK;
|
value = QPNP_PWM_SYNC_VALUE & QPNP_PWM_SYNC_MASK;
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_write(chip->regmap,
|
||||||
chip->spmi_dev->sid,
|
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
||||||
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
SPMI_LPG_PWM_SYNC),
|
||||||
SPMI_LPG_PWM_SYNC), &value, 1);
|
value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -735,17 +738,18 @@ static int qpnp_lpg_configure_pwm(struct qpnp_pwm_chip *chip)
|
||||||
int rc;
|
int rc;
|
||||||
u8 value, mask;
|
u8 value, mask;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
SPMI_LPG_REG_ADDR(lpg_config->base_addr, QPNP_LPG_PWM_SIZE_CLK),
|
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
||||||
&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK], 1);
|
QPNP_LPG_PWM_SIZE_CLK),
|
||||||
|
*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
||||||
QPNP_LPG_PWM_FREQ_PREDIV_CLK),
|
QPNP_LPG_PWM_FREQ_PREDIV_CLK),
|
||||||
&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK], 1);
|
*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK]);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -1013,8 +1017,7 @@ static int qpnp_dtest_config(struct qpnp_pwm_chip *chip, bool enable)
|
||||||
|
|
||||||
addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr, QPNP_LPG_SEC_ACCESS);
|
addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr, QPNP_LPG_SEC_ACCESS);
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_write(chip->regmap, addr, value);
|
||||||
chip->spmi_dev->sid, addr, &value, 1);
|
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Couldn't set the access for test mode\n");
|
pr_err("Couldn't set the access for test mode\n");
|
||||||
|
@ -1032,8 +1035,7 @@ static int qpnp_dtest_config(struct qpnp_pwm_chip *chip, bool enable)
|
||||||
pr_debug("Setting TEST mode for channel %d addr:%x value: %x\n",
|
pr_debug("Setting TEST mode for channel %d addr:%x value: %x\n",
|
||||||
chip->channel_id, addr, value);
|
chip->channel_id, addr, value);
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_write(chip->regmap, addr, value);
|
||||||
chip->spmi_dev->sid, addr, &value, 1);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1483,10 +1485,10 @@ int pwm_config_period(struct pwm_device *pwm,
|
||||||
|
|
||||||
qpnp_lpg_save_period(chip);
|
qpnp_lpg_save_period(chip);
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
||||||
QPNP_LPG_PWM_SIZE_CLK),
|
QPNP_LPG_PWM_SIZE_CLK),
|
||||||
&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK], 1);
|
*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]);
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Write failed: QPNP_LPG_PWM_SIZE_CLK register, rc: %d\n",
|
pr_err("Write failed: QPNP_LPG_PWM_SIZE_CLK register, rc: %d\n",
|
||||||
|
@ -1494,10 +1496,10 @@ int pwm_config_period(struct pwm_device *pwm,
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl, chip->spmi_dev->sid,
|
rc = regmap_write(chip->regmap,
|
||||||
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
SPMI_LPG_REG_ADDR(lpg_config->base_addr,
|
||||||
QPNP_LPG_PWM_FREQ_PREDIV_CLK),
|
QPNP_LPG_PWM_FREQ_PREDIV_CLK),
|
||||||
&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK], 1);
|
*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK]);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Failed to write to QPNP_LPG_PWM_FREQ_PREDIV_CLK\n");
|
pr_err("Failed to write to QPNP_LPG_PWM_FREQ_PREDIV_CLK\n");
|
||||||
pr_err("register, rc = %d\n", rc);
|
pr_err("register, rc = %d\n", rc);
|
||||||
|
@ -1779,26 +1781,27 @@ out:
|
||||||
static int qpnp_lpg_get_rev_subtype(struct qpnp_pwm_chip *chip)
|
static int qpnp_lpg_get_rev_subtype(struct qpnp_pwm_chip *chip)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
|
rc = regmap_read(chip->regmap,
|
||||||
chip->spmi_dev->sid,
|
chip->lpg_config.base_addr + SPMI_LPG_SUB_TYPE_OFFSET,
|
||||||
chip->lpg_config.base_addr + SPMI_LPG_SUB_TYPE_OFFSET,
|
&val);
|
||||||
&chip->sub_type, 1);
|
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Couldn't read subtype rc: %d\n", rc);
|
pr_err("Couldn't read subtype rc: %d\n", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
chip->sub_type = (u8)val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
|
rc = regmap_read(chip->regmap,
|
||||||
chip->spmi_dev->sid,
|
chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
|
||||||
chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
|
&val);
|
||||||
(u8 *) &chip->revision, 1);
|
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Couldn't read revision2 rc: %d\n", rc);
|
pr_err("Couldn't read revision2 rc: %d\n", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
chip->revision = (u8)val;
|
||||||
|
|
||||||
if (chip->revision < QPNP_LPG_REVISION_0 ||
|
if (chip->revision < QPNP_LPG_REVISION_0 ||
|
||||||
chip->revision > QPNP_LPG_REVISION_1) {
|
chip->revision > QPNP_LPG_REVISION_1) {
|
||||||
|
@ -1822,16 +1825,17 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in lpg device elements based on values found in device tree. */
|
/* Fill in lpg device elements based on values found in device tree. */
|
||||||
static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
static int qpnp_parse_dt_config(struct platform_device *pdev,
|
||||||
struct qpnp_pwm_chip *chip)
|
struct qpnp_pwm_chip *chip)
|
||||||
{
|
{
|
||||||
int rc, enable, lut_entry_size, list_size, i;
|
int rc, enable, lut_entry_size, list_size, i;
|
||||||
const char *lable;
|
const char *lable;
|
||||||
struct resource *res;
|
const __be32 *prop;
|
||||||
|
u64 size;
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
int found_pwm_subnode = 0;
|
int found_pwm_subnode = 0;
|
||||||
int found_lpg_subnode = 0;
|
int found_lpg_subnode = 0;
|
||||||
struct device_node *of_node = spmi->dev.of_node;
|
struct device_node *of_node = pdev->dev.of_node;
|
||||||
struct qpnp_lpg_config *lpg_config = &chip->lpg_config;
|
struct qpnp_lpg_config *lpg_config = &chip->lpg_config;
|
||||||
struct qpnp_lut_config *lut_config = &lpg_config->lut_config;
|
struct qpnp_lut_config *lut_config = &lpg_config->lut_config;
|
||||||
struct _qpnp_pwm_config *pwm_config = &chip->pwm_config;
|
struct _qpnp_pwm_config *pwm_config = &chip->pwm_config;
|
||||||
|
@ -1841,7 +1845,7 @@ static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
||||||
rc = of_property_read_u32(of_node, "qcom,channel-id",
|
rc = of_property_read_u32(of_node, "qcom,channel-id",
|
||||||
&chip->channel_id);
|
&chip->channel_id);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing LPG channel id\n",
|
dev_err(&pdev->dev, "%s: node is missing LPG channel id\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1900,34 +1904,36 @@ static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
||||||
}
|
}
|
||||||
|
|
||||||
pwm_config->force_pwm_size = force_pwm_size;
|
pwm_config->force_pwm_size = force_pwm_size;
|
||||||
res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
|
||||||
QPNP_LPG_CHANNEL_BASE);
|
|
||||||
if (!res) {
|
|
||||||
dev_err(&spmi->dev, "%s: node is missing base address\n",
|
|
||||||
__func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lpg_config->base_addr = res->start;
|
|
||||||
|
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_LPG_CHANNEL_BASE,
|
||||||
|
0, 0);
|
||||||
|
if (!prop) {
|
||||||
|
dev_err(&pdev->dev, "Couldnt find channel's base addr rc %d\n",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
lpg_config->base_addr = be32_to_cpu(*prop);
|
||||||
|
|
||||||
rc = qpnp_lpg_get_rev_subtype(chip);
|
rc = qpnp_lpg_get_rev_subtype(chip);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
res = spmi_get_resource_byname(spmi, NULL, IORESOURCE_MEM,
|
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_LPG_LUT_BASE,
|
||||||
QPNP_LPG_LUT_BASE);
|
&size, 0);
|
||||||
if (!res) {
|
if (!prop) {
|
||||||
chip->flags |= QPNP_PWM_LUT_NOT_SUPPORTED;
|
chip->flags |= QPNP_PWM_LUT_NOT_SUPPORTED;
|
||||||
} else {
|
} else {
|
||||||
lpg_config->lut_base_addr = res->start;
|
lpg_config->lut_base_addr = be32_to_cpu(*prop);
|
||||||
/* Each entry of LUT is of 2 bytes for generic LUT and of 1 byte
|
/*
|
||||||
|
* Each entry of LUT is of 2 bytes for generic LUT and of 1 byte
|
||||||
* for KPDBL/GLED LUT.
|
* for KPDBL/GLED LUT.
|
||||||
*/
|
*/
|
||||||
lpg_config->lut_size = resource_size(res) >> 1;
|
lpg_config->lut_size = size >> 1;
|
||||||
lut_entry_size = sizeof(u16);
|
lut_entry_size = sizeof(u16);
|
||||||
|
|
||||||
if (pwm_config->supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
|
if (pwm_config->supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
|
||||||
lpg_config->lut_size = resource_size(res);
|
lpg_config->lut_size = size;
|
||||||
lut_entry_size = sizeof(u8);
|
lut_entry_size = sizeof(u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1964,7 +1970,7 @@ static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
||||||
for_each_child_of_node(of_node, node) {
|
for_each_child_of_node(of_node, node) {
|
||||||
rc = of_property_read_string(node, "label", &lable);
|
rc = of_property_read_string(node, "label", &lable);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: Missing lable property\n",
|
dev_err(&pdev->dev, "%s: Missing lable property\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1980,7 +1986,8 @@ static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
||||||
goto out;
|
goto out;
|
||||||
found_lpg_subnode = 1;
|
found_lpg_subnode = 1;
|
||||||
} else {
|
} else {
|
||||||
dev_err(&spmi->dev, "%s: Invalid value for lable prop",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: Invalid value for lable prop",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +1998,7 @@ static int qpnp_parse_dt_config(struct spmi_device *spmi,
|
||||||
|
|
||||||
if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
|
if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
|
||||||
(enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
|
(enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
|
||||||
dev_err(&spmi->dev, "%s: Invalid mode select\n", __func__);
|
dev_err(&pdev->dev, "%s: Invalid mode select\n", __func__);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -2019,7 +2026,7 @@ static struct pwm_ops qpnp_pwm_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int qpnp_pwm_probe(struct spmi_device *spmi)
|
static int qpnp_pwm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_pwm_chip *pwm_chip;
|
struct qpnp_pwm_chip *pwm_chip;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -2029,18 +2036,23 @@ static int qpnp_pwm_probe(struct spmi_device *spmi)
|
||||||
pr_err("kzalloc() failed.\n");
|
pr_err("kzalloc() failed.\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
pwm_chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!pwm_chip->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_init(&pwm_chip->lpg_lock);
|
spin_lock_init(&pwm_chip->lpg_lock);
|
||||||
|
|
||||||
pwm_chip->spmi_dev = spmi;
|
pwm_chip->pdev = pdev;
|
||||||
dev_set_drvdata(&spmi->dev, pwm_chip);
|
dev_set_drvdata(&pdev->dev, pwm_chip);
|
||||||
|
|
||||||
rc = qpnp_parse_dt_config(spmi, pwm_chip);
|
rc = qpnp_parse_dt_config(pdev, pwm_chip);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto failed_config;
|
goto failed_config;
|
||||||
|
|
||||||
pwm_chip->chip.dev = &spmi->dev;
|
pwm_chip->chip.dev = &pdev->dev;
|
||||||
pwm_chip->chip.ops = &qpnp_pwm_ops;
|
pwm_chip->chip.ops = &qpnp_pwm_ops;
|
||||||
pwm_chip->chip.base = -1;
|
pwm_chip->chip.base = -1;
|
||||||
pwm_chip->chip.npwm = 1;
|
pwm_chip->chip.npwm = 1;
|
||||||
|
@ -2059,19 +2071,19 @@ static int qpnp_pwm_probe(struct spmi_device *spmi)
|
||||||
failed_insert:
|
failed_insert:
|
||||||
kfree(pwm_chip->lpg_config.lut_config.duty_pct_list);
|
kfree(pwm_chip->lpg_config.lut_config.duty_pct_list);
|
||||||
failed_config:
|
failed_config:
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
kfree(pwm_chip);
|
kfree(pwm_chip);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_pwm_remove(struct spmi_device *spmi)
|
static int qpnp_pwm_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_pwm_chip *pwm_chip;
|
struct qpnp_pwm_chip *pwm_chip;
|
||||||
struct qpnp_lpg_config *lpg_config;
|
struct qpnp_lpg_config *lpg_config;
|
||||||
|
|
||||||
pwm_chip = dev_get_drvdata(&spmi->dev);
|
pwm_chip = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
if (pwm_chip) {
|
if (pwm_chip) {
|
||||||
lpg_config = &pwm_chip->lpg_config;
|
lpg_config = &pwm_chip->lpg_config;
|
||||||
|
@ -2088,17 +2100,17 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_lpg_id[] = {
|
static const struct platform_device_id qpnp_lpg_id[] = {
|
||||||
{ QPNP_LPG_DRIVER_NAME, 0 },
|
{ QPNP_LPG_DRIVER_NAME, 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, qpnp_lpg_id);
|
MODULE_DEVICE_TABLE(spmi, qpnp_lpg_id);
|
||||||
|
|
||||||
static struct spmi_driver qpnp_lpg_driver = {
|
static struct platform_driver qpnp_lpg_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_LPG_DRIVER_NAME,
|
.name = QPNP_LPG_DRIVER_NAME,
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
.probe = qpnp_pwm_probe,
|
.probe = qpnp_pwm_probe,
|
||||||
.remove = qpnp_pwm_remove,
|
.remove = qpnp_pwm_remove,
|
||||||
|
@ -2110,12 +2122,12 @@ static struct spmi_driver qpnp_lpg_driver = {
|
||||||
*/
|
*/
|
||||||
int __init qpnp_lpg_init(void)
|
int __init qpnp_lpg_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_lpg_driver);
|
return platform_driver_register(&qpnp_lpg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_lpg_exit(void)
|
static void __exit qpnp_lpg_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_lpg_driver);
|
platform_driver_unregister(&qpnp_lpg_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("QPNP PMIC LPG driver");
|
MODULE_DESCRIPTION("QPNP PMIC LPG driver");
|
||||||
|
|
|
@ -796,8 +796,7 @@ config REGULATOR_RPM_SMD
|
||||||
application processor over SMD.
|
application processor over SMD.
|
||||||
|
|
||||||
config REGULATOR_QPNP
|
config REGULATOR_QPNP
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
tristate "Qualcomm QPNP regulator support"
|
tristate "Qualcomm QPNP regulator support"
|
||||||
help
|
help
|
||||||
This driver supports voltage regulators in Qualcomm PMIC chips which
|
This driver supports voltage regulators in Qualcomm PMIC chips which
|
||||||
|
@ -806,8 +805,7 @@ config REGULATOR_QPNP
|
||||||
regulators. They also provide voltage switches and boost regulators.
|
regulators. They also provide voltage switches and boost regulators.
|
||||||
|
|
||||||
config REGULATOR_QPNP_LABIBB
|
config REGULATOR_QPNP_LABIBB
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
tristate "Qualcomm Technologies, Inc QPNP LAB/IBB regulator support"
|
tristate "Qualcomm Technologies, Inc QPNP LAB/IBB regulator support"
|
||||||
help
|
help
|
||||||
This driver supports voltage regulators in Qualcomm Technologies, Inc
|
This driver supports voltage regulators in Qualcomm Technologies, Inc
|
||||||
|
@ -819,7 +817,7 @@ config REGULATOR_QPNP_LABIBB
|
||||||
|
|
||||||
config REGULATOR_SPM
|
config REGULATOR_SPM
|
||||||
bool "SPM regulator driver"
|
bool "SPM regulator driver"
|
||||||
depends on (SPMI || MSM_SPMI) && OF_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
Enable support for the SPM regulator driver which is used for
|
Enable support for the SPM regulator driver which is used for
|
||||||
setting voltages of processor supply regulators via the SPM module
|
setting voltages of processor supply regulators via the SPM module
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
|
@ -428,7 +430,8 @@ struct ibb_regulator {
|
||||||
|
|
||||||
struct qpnp_labibb {
|
struct qpnp_labibb {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
u16 lab_base;
|
u16 lab_base;
|
||||||
u16 ibb_base;
|
u16 ibb_base;
|
||||||
struct lab_regulator lab_vreg;
|
struct lab_regulator lab_vreg;
|
||||||
|
@ -483,18 +486,18 @@ qpnp_labibb_read(struct qpnp_labibb *labibb, u8 *val,
|
||||||
u16 base, int count)
|
u16 base, int count)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct spmi_device *spmi = labibb->spmi;
|
struct platform_device *pdev = labibb->pdev;
|
||||||
|
|
||||||
if (base == 0) {
|
if (base == 0) {
|
||||||
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
||||||
base, spmi->sid, rc);
|
base, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count);
|
rc = regmap_bulk_read(labibb->regmap, base, val, count);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base,
|
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base,
|
||||||
spmi->sid, rc);
|
to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -505,18 +508,18 @@ qpnp_labibb_write(struct qpnp_labibb *labibb, u16 base,
|
||||||
u8 *val, int count)
|
u8 *val, int count)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct spmi_device *spmi = labibb->spmi;
|
struct platform_device *pdev = labibb->pdev;
|
||||||
|
|
||||||
if (base == 0) {
|
if (base == 0) {
|
||||||
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
|
||||||
base, spmi->sid, rc);
|
base, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val, count);
|
rc = regmap_bulk_write(labibb->regmap, base, val, count);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("write failed base=0x%02x sid=0x%02x rc=%d\n",
|
pr_err("write failed base=0x%02x sid=0x%02x rc=%d\n",
|
||||||
base, spmi->sid, rc);
|
base, to_spmi_device(pdev->dev.parent)->usid, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,21 +531,8 @@ qpnp_labibb_masked_write(struct qpnp_labibb *labibb, u16 base,
|
||||||
u8 mask, u8 val)
|
u8 mask, u8 val)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg;
|
|
||||||
|
|
||||||
rc = qpnp_labibb_read(labibb, ®, base, 1);
|
rc = regmap_update_bits(labibb->regmap, base, mask, val);
|
||||||
if (rc) {
|
|
||||||
pr_err("spmi read failed: addr=%03X, rc=%d\n", base, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
pr_debug("addr = 0x%x read 0x%x\n", base, reg);
|
|
||||||
|
|
||||||
reg &= ~mask;
|
|
||||||
reg |= val & mask;
|
|
||||||
|
|
||||||
pr_debug("Writing 0x%x\n", reg);
|
|
||||||
|
|
||||||
rc = qpnp_labibb_write(labibb, base, ®, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("spmi write failed: addr=%03X, rc=%d\n", base, rc);
|
pr_err("spmi write failed: addr=%03X, rc=%d\n", base, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -2341,24 +2331,29 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
|
static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_labibb *labibb;
|
struct qpnp_labibb *labibb;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
const char *mode_name;
|
const char *mode_name;
|
||||||
u8 type;
|
u8 type;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
labibb = devm_kzalloc(&spmi->dev,
|
labibb = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(struct qpnp_labibb), GFP_KERNEL);
|
sizeof(struct qpnp_labibb), GFP_KERNEL);
|
||||||
if (labibb == NULL) {
|
if (labibb == NULL) {
|
||||||
pr_err("labibb allocation failed.\n");
|
pr_err("labibb allocation failed.\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
labibb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!labibb->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
labibb->dev = &(spmi->dev);
|
labibb->dev = &(pdev->dev);
|
||||||
labibb->spmi = spmi;
|
labibb->pdev = pdev;
|
||||||
|
|
||||||
rc = of_property_read_string(labibb->dev->of_node,
|
rc = of_property_read_string(labibb->dev->of_node,
|
||||||
"qpnp,qpnp-labibb-mode", &mode_name);
|
"qpnp,qpnp-labibb-mode", &mode_name);
|
||||||
|
@ -2393,22 +2388,21 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
spmi_for_each_container_dev(spmi_resource, spmi) {
|
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
|
||||||
if (!spmi_resource) {
|
pr_err("no child nodes\n");
|
||||||
pr_err("qpnp_labibb: spmi resource absent\n");
|
return -ENXIO;
|
||||||
return -ENXIO;
|
}
|
||||||
}
|
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||||
|
rc = of_property_read_u32(child, "reg", &base);
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
if (rc < 0) {
|
||||||
IORESOURCE_MEM, 0);
|
dev_err(&pdev->dev,
|
||||||
if (!(resource && resource->start)) {
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
pr_err("node %s IO resource absent!\n",
|
child->full_name, rc);
|
||||||
spmi->dev.of_node->full_name);
|
return rc;
|
||||||
return -ENXIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_labibb_read(labibb, &type,
|
rc = qpnp_labibb_read(labibb, &type,
|
||||||
resource->start + REG_PERPH_TYPE, 1);
|
base + REG_PERPH_TYPE, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("Peripheral type read failed rc=%d\n", rc);
|
pr_err("Peripheral type read failed rc=%d\n", rc);
|
||||||
goto fail_registration;
|
goto fail_registration;
|
||||||
|
@ -2416,17 +2410,15 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QPNP_LAB_TYPE:
|
case QPNP_LAB_TYPE:
|
||||||
labibb->lab_base = resource->start;
|
labibb->lab_base = base;
|
||||||
rc = register_qpnp_lab_regulator(labibb,
|
rc = register_qpnp_lab_regulator(labibb, child);
|
||||||
spmi_resource->of_node);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto fail_registration;
|
goto fail_registration;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QPNP_IBB_TYPE:
|
case QPNP_IBB_TYPE:
|
||||||
labibb->ibb_base = resource->start;
|
labibb->ibb_base = base;
|
||||||
rc = register_qpnp_ibb_regulator(labibb,
|
rc = register_qpnp_ibb_regulator(labibb, child);
|
||||||
spmi_resource->of_node);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto fail_registration;
|
goto fail_registration;
|
||||||
break;
|
break;
|
||||||
|
@ -2439,7 +2431,7 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, labibb);
|
dev_set_drvdata(&pdev->dev, labibb);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_registration:
|
fail_registration:
|
||||||
|
@ -2451,9 +2443,9 @@ fail_registration:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_labibb_regulator_remove(struct spmi_device *spmi)
|
static int qpnp_labibb_regulator_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_labibb *labibb = dev_get_drvdata(&spmi->dev);
|
struct qpnp_labibb *labibb = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
if (labibb) {
|
if (labibb) {
|
||||||
if (labibb->lab_vreg.rdev)
|
if (labibb->lab_vreg.rdev)
|
||||||
|
@ -2469,10 +2461,10 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_labibb_regulator_driver = {
|
static struct platform_driver qpnp_labibb_regulator_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_LABIBB_REGULATOR_DRIVER_NAME,
|
.name = QPNP_LABIBB_REGULATOR_DRIVER_NAME,
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
},
|
},
|
||||||
.probe = qpnp_labibb_regulator_probe,
|
.probe = qpnp_labibb_regulator_probe,
|
||||||
.remove = qpnp_labibb_regulator_remove,
|
.remove = qpnp_labibb_regulator_remove,
|
||||||
|
@ -2480,13 +2472,13 @@ static struct spmi_driver qpnp_labibb_regulator_driver = {
|
||||||
|
|
||||||
static int __init qpnp_labibb_regulator_init(void)
|
static int __init qpnp_labibb_regulator_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_labibb_regulator_driver);
|
return platform_driver_register(&qpnp_labibb_regulator_driver);
|
||||||
}
|
}
|
||||||
arch_initcall(qpnp_labibb_regulator_init);
|
arch_initcall(qpnp_labibb_regulator_init);
|
||||||
|
|
||||||
static void __exit qpnp_labibb_regulator_exit(void)
|
static void __exit qpnp_labibb_regulator_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_labibb_regulator_driver);
|
platform_driver_unregister(&qpnp_labibb_regulator_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_labibb_regulator_exit);
|
module_exit(qpnp_labibb_regulator_exit);
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -282,7 +284,8 @@ struct qpnp_regulator_mapping {
|
||||||
struct qpnp_regulator {
|
struct qpnp_regulator {
|
||||||
struct regulator_desc rdesc;
|
struct regulator_desc rdesc;
|
||||||
struct delayed_work ocp_work;
|
struct delayed_work ocp_work;
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
struct regulator_dev *rdev;
|
struct regulator_dev *rdev;
|
||||||
struct qpnp_voltage_set_points *set_points;
|
struct qpnp_voltage_set_points *set_points;
|
||||||
enum qpnp_regulator_logical_type logical_type;
|
enum qpnp_regulator_logical_type logical_type;
|
||||||
|
@ -483,15 +486,15 @@ static inline int qpnp_vreg_read(struct qpnp_regulator *vreg, u16 addr, u8 *buf,
|
||||||
char str[DEBUG_PRINT_BUFFER_SIZE];
|
char str[DEBUG_PRINT_BUFFER_SIZE];
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_bulk_read(vreg->regmap, vreg->base_addr + addr, buf, len);
|
||||||
vreg->base_addr + addr, buf, len);
|
|
||||||
|
|
||||||
if (!rc && (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_READS)) {
|
if (!rc && (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_READS)) {
|
||||||
str[0] = '\0';
|
str[0] = '\0';
|
||||||
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
|
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
|
||||||
pr_info(" %-11s: read(0x%04X), sid=%d, len=%d; %s\n",
|
pr_info(" %-11s: read(0x%04X), sid=%d, len=%d; %s\n",
|
||||||
vreg->rdesc.name, vreg->base_addr + addr,
|
vreg->rdesc.name, vreg->base_addr + addr,
|
||||||
vreg->spmi_dev->sid, len, str);
|
to_spmi_device(vreg->pdev->dev.parent)->usid, len,
|
||||||
|
str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -508,11 +511,11 @@ static inline int qpnp_vreg_write(struct qpnp_regulator *vreg, u16 addr,
|
||||||
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
|
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
|
||||||
pr_info("%-11s: write(0x%04X), sid=%d, len=%d; %s\n",
|
pr_info("%-11s: write(0x%04X), sid=%d, len=%d; %s\n",
|
||||||
vreg->rdesc.name, vreg->base_addr + addr,
|
vreg->rdesc.name, vreg->base_addr + addr,
|
||||||
vreg->spmi_dev->sid, len, str);
|
to_spmi_device(vreg->pdev->dev.parent)->usid, len,
|
||||||
|
str);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl,
|
rc = regmap_bulk_write(vreg->regmap, vreg->base_addr + addr, buf, len);
|
||||||
vreg->spmi_dev->sid, vreg->base_addr + addr, buf, len);
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
vreg->write_count += len;
|
vreg->write_count += len;
|
||||||
|
|
||||||
|
@ -1511,7 +1514,7 @@ static const struct qpnp_regulator_mapping supported_regulators[] = {
|
||||||
static int qpnp_regulator_match(struct qpnp_regulator *vreg)
|
static int qpnp_regulator_match(struct qpnp_regulator *vreg)
|
||||||
{
|
{
|
||||||
const struct qpnp_regulator_mapping *mapping;
|
const struct qpnp_regulator_mapping *mapping;
|
||||||
struct device_node *node = vreg->spmi_dev->dev.of_node;
|
struct device_node *node = vreg->pdev->dev.of_node;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
u32 type_reg[2], dig_major_rev;
|
u32 type_reg[2], dig_major_rev;
|
||||||
u8 version[QPNP_COMMON_REG_SUBTYPE - QPNP_COMMON_REG_DIG_MAJOR_REV + 1];
|
u8 version[QPNP_COMMON_REG_SUBTYPE - QPNP_COMMON_REG_DIG_MAJOR_REV + 1];
|
||||||
|
@ -1834,26 +1837,27 @@ static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in pdata elements based on values found in device tree. */
|
/* Fill in pdata elements based on values found in device tree. */
|
||||||
static int qpnp_regulator_get_dt_config(struct spmi_device *spmi,
|
static int qpnp_regulator_get_dt_config(struct platform_device *pdev,
|
||||||
struct qpnp_regulator_platform_data *pdata)
|
struct qpnp_regulator_platform_data *pdata)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
pdata->init_data.constraints.input_uV
|
pdata->init_data.constraints.input_uV
|
||||||
= pdata->init_data.constraints.max_uV;
|
= pdata->init_data.constraints.max_uV;
|
||||||
|
|
||||||
res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
if (!res) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing base address\n",
|
dev_err(&pdev->dev,
|
||||||
__func__);
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
return -EINVAL;
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
pdata->base_addr = res->start;
|
pdata->base_addr = base;
|
||||||
|
|
||||||
/* OCP IRQ is optional so ignore get errors. */
|
/* OCP IRQ is optional so ignore get errors. */
|
||||||
pdata->ocp_irq = spmi_get_irq_byname(spmi, NULL, "ocp");
|
pdata->ocp_irq = platform_get_irq_byname(pdev, "ocp");
|
||||||
if (pdata->ocp_irq < 0)
|
if (pdata->ocp_irq < 0)
|
||||||
pdata->ocp_irq = 0;
|
pdata->ocp_irq = 0;
|
||||||
|
|
||||||
|
@ -1904,7 +1908,7 @@ static struct of_device_id spmi_match_table[];
|
||||||
|
|
||||||
#define MAX_NAME_LEN 127
|
#define MAX_NAME_LEN 127
|
||||||
|
|
||||||
static int qpnp_regulator_probe(struct spmi_device *spmi)
|
static int qpnp_regulator_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct regulator_config reg_config = {};
|
struct regulator_config reg_config = {};
|
||||||
struct qpnp_regulator_platform_data *pdata;
|
struct qpnp_regulator_platform_data *pdata;
|
||||||
|
@ -1917,20 +1921,23 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
bool is_dt;
|
bool is_dt;
|
||||||
|
|
||||||
vreg = kzalloc(sizeof(struct qpnp_regulator), GFP_KERNEL);
|
vreg = kzalloc(sizeof(struct qpnp_regulator), GFP_KERNEL);
|
||||||
if (!vreg) {
|
if (!vreg)
|
||||||
dev_err(&spmi->dev, "%s: Can't allocate qpnp_regulator\n",
|
|
||||||
__func__);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
vreg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!vreg->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_dt = of_match_device(spmi_match_table, &spmi->dev);
|
is_dt = of_match_device(spmi_match_table, &pdev->dev);
|
||||||
|
|
||||||
/* Check if device tree is in use. */
|
/* Check if device tree is in use. */
|
||||||
if (is_dt) {
|
if (is_dt) {
|
||||||
init_data = of_get_regulator_init_data(&spmi->dev,
|
init_data = of_get_regulator_init_data(&pdev->dev,
|
||||||
spmi->dev.of_node);
|
pdev->dev.of_node);
|
||||||
if (!init_data) {
|
if (!init_data) {
|
||||||
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
|
dev_err(&pdev->dev, "%s: unable to allocate memory\n",
|
||||||
__func__);
|
__func__);
|
||||||
kfree(vreg);
|
kfree(vreg);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1940,12 +1947,12 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
memcpy(&of_pdata.init_data, init_data,
|
memcpy(&of_pdata.init_data, init_data,
|
||||||
sizeof(struct regulator_init_data));
|
sizeof(struct regulator_init_data));
|
||||||
|
|
||||||
if (of_get_property(spmi->dev.of_node, "parent-supply", NULL))
|
if (of_get_property(pdev->dev.of_node, "parent-supply", NULL))
|
||||||
of_pdata.init_data.supply_regulator = "parent";
|
of_pdata.init_data.supply_regulator = "parent";
|
||||||
|
|
||||||
rc = qpnp_regulator_get_dt_config(spmi, &of_pdata);
|
rc = qpnp_regulator_get_dt_config(pdev, &of_pdata);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: DT parsing failed, rc=%d\n",
|
dev_err(&pdev->dev, "%s: DT parsing failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
kfree(vreg);
|
kfree(vreg);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1953,17 +1960,17 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
pdata = &of_pdata;
|
pdata = &of_pdata;
|
||||||
} else {
|
} else {
|
||||||
pdata = spmi->dev.platform_data;
|
pdata = pdev->dev.platform_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata == NULL) {
|
if (pdata == NULL) {
|
||||||
dev_err(&spmi->dev, "%s: no platform data specified\n",
|
dev_err(&pdev->dev, "%s: no platform data specified\n",
|
||||||
__func__);
|
__func__);
|
||||||
kfree(vreg);
|
kfree(vreg);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
vreg->spmi_dev = spmi;
|
vreg->pdev = pdev;
|
||||||
vreg->prev_write_count = -1;
|
vreg->prev_write_count = -1;
|
||||||
vreg->write_count = 0;
|
vreg->write_count = 0;
|
||||||
vreg->base_addr = pdata->base_addr;
|
vreg->base_addr = pdata->base_addr;
|
||||||
|
@ -1980,14 +1987,14 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
vreg->ocp_retry_delay_ms = QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS;
|
vreg->ocp_retry_delay_ms = QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS;
|
||||||
|
|
||||||
rdesc = &vreg->rdesc;
|
rdesc = &vreg->rdesc;
|
||||||
rdesc->id = spmi->ctrl->nr;
|
rdesc->id = to_spmi_device(pdev->dev.parent)->ctrl->nr;
|
||||||
rdesc->owner = THIS_MODULE;
|
rdesc->owner = THIS_MODULE;
|
||||||
rdesc->type = REGULATOR_VOLTAGE;
|
rdesc->type = REGULATOR_VOLTAGE;
|
||||||
|
|
||||||
reg_name = kzalloc(strnlen(pdata->init_data.constraints.name,
|
reg_name = kzalloc(strnlen(pdata->init_data.constraints.name,
|
||||||
MAX_NAME_LEN) + 1, GFP_KERNEL);
|
MAX_NAME_LEN) + 1, GFP_KERNEL);
|
||||||
if (!reg_name) {
|
if (!reg_name) {
|
||||||
dev_err(&spmi->dev, "%s: Can't allocate regulator name\n",
|
dev_err(&pdev->dev, "%s: Can't allocate regulator name\n",
|
||||||
__func__);
|
__func__);
|
||||||
kfree(vreg);
|
kfree(vreg);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1996,7 +2003,7 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
strnlen(pdata->init_data.constraints.name, MAX_NAME_LEN) + 1);
|
strnlen(pdata->init_data.constraints.name, MAX_NAME_LEN) + 1);
|
||||||
rdesc->name = reg_name;
|
rdesc->name = reg_name;
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, vreg);
|
dev_set_drvdata(&pdev->dev, vreg);
|
||||||
|
|
||||||
rc = qpnp_regulator_match(vreg);
|
rc = qpnp_regulator_match(vreg);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -2029,7 +2036,7 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
vreg->ocp_irq = 0;
|
vreg->ocp_irq = 0;
|
||||||
|
|
||||||
if (vreg->ocp_irq) {
|
if (vreg->ocp_irq) {
|
||||||
rc = devm_request_irq(&spmi->dev, vreg->ocp_irq,
|
rc = devm_request_irq(&pdev->dev, vreg->ocp_irq,
|
||||||
qpnp_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
|
qpnp_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
|
||||||
vreg);
|
vreg);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
@ -2041,10 +2048,10 @@ static int qpnp_regulator_probe(struct spmi_device *spmi)
|
||||||
INIT_DELAYED_WORK(&vreg->ocp_work, qpnp_regulator_vs_ocp_work);
|
INIT_DELAYED_WORK(&vreg->ocp_work, qpnp_regulator_vs_ocp_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_config.dev = &spmi->dev;
|
reg_config.dev = &pdev->dev;
|
||||||
reg_config.init_data = &pdata->init_data;
|
reg_config.init_data = &pdata->init_data;
|
||||||
reg_config.driver_data = vreg;
|
reg_config.driver_data = vreg;
|
||||||
reg_config.of_node = spmi->dev.of_node;
|
reg_config.of_node = pdev->dev.of_node;
|
||||||
vreg->rdev = regulator_register(rdesc, ®_config);
|
vreg->rdev = regulator_register(rdesc, ®_config);
|
||||||
if (IS_ERR(vreg->rdev)) {
|
if (IS_ERR(vreg->rdev)) {
|
||||||
rc = PTR_ERR(vreg->rdev);
|
rc = PTR_ERR(vreg->rdev);
|
||||||
|
@ -2071,12 +2078,12 @@ bail:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_regulator_remove(struct spmi_device *spmi)
|
static int qpnp_regulator_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_regulator *vreg;
|
struct qpnp_regulator *vreg;
|
||||||
|
|
||||||
vreg = dev_get_drvdata(&spmi->dev);
|
vreg = dev_get_drvdata(&pdev->dev);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
if (vreg) {
|
if (vreg) {
|
||||||
regulator_unregister(vreg->rdev);
|
regulator_unregister(vreg->rdev);
|
||||||
|
@ -2094,17 +2101,17 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_regulator_id[] = {
|
static const struct platform_device_id qpnp_regulator_id[] = {
|
||||||
{ QPNP_REGULATOR_DRIVER_NAME, 0 },
|
{ QPNP_REGULATOR_DRIVER_NAME, 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, qpnp_regulator_id);
|
MODULE_DEVICE_TABLE(spmi, qpnp_regulator_id);
|
||||||
|
|
||||||
static struct spmi_driver qpnp_regulator_driver = {
|
static struct platform_driver qpnp_regulator_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_REGULATOR_DRIVER_NAME,
|
.name = QPNP_REGULATOR_DRIVER_NAME,
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
.probe = qpnp_regulator_probe,
|
.probe = qpnp_regulator_probe,
|
||||||
.remove = qpnp_regulator_remove,
|
.remove = qpnp_regulator_remove,
|
||||||
|
@ -2154,13 +2161,13 @@ int __init qpnp_regulator_init(void)
|
||||||
|
|
||||||
qpnp_regulator_set_point_init();
|
qpnp_regulator_set_point_init();
|
||||||
|
|
||||||
return spmi_driver_register(&qpnp_regulator_driver);
|
return platform_driver_register(&qpnp_regulator_driver);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qpnp_regulator_init);
|
EXPORT_SYMBOL(qpnp_regulator_init);
|
||||||
|
|
||||||
static void __exit qpnp_regulator_exit(void)
|
static void __exit qpnp_regulator_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_regulator_driver);
|
platform_driver_unregister(&qpnp_regulator_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("QPNP PMIC regulator driver");
|
MODULE_DESCRIPTION("QPNP PMIC regulator driver");
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
|
@ -122,7 +124,8 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
|
||||||
struct spm_vreg {
|
struct spm_vreg {
|
||||||
struct regulator_desc rdesc;
|
struct regulator_desc rdesc;
|
||||||
struct regulator_dev *rdev;
|
struct regulator_dev *rdev;
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
const struct voltage_range *range;
|
const struct voltage_range *range;
|
||||||
int uV;
|
int uV;
|
||||||
int last_set_uV;
|
int last_set_uV;
|
||||||
|
@ -196,14 +199,18 @@ static int qpnp_smps_read_voltage(struct spm_vreg *vreg)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg = 0;
|
u8 reg = 0;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_read(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, ®, 1);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
|
||||||
|
&val);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read voltage setpoint register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read voltage setpoint register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
reg = (u8)val;
|
||||||
|
|
||||||
vreg->last_set_vlevel = reg;
|
vreg->last_set_vlevel = reg;
|
||||||
vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, reg);
|
vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, reg);
|
||||||
|
@ -215,10 +222,11 @@ static int qpnp_smps_set_mode(struct spm_vreg *vreg, u8 mode)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_write(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, &mode, 1);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, mode);
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not write to mode register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not write to mode register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -275,12 +283,11 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
|
||||||
if (unlikely(vreg->bypass_spm || spm_failed)) {
|
if (unlikely(vreg->bypass_spm || spm_failed)) {
|
||||||
/* Set voltage control register via SPMI. */
|
/* Set voltage control register via SPMI. */
|
||||||
reg = vlevel;
|
reg = vlevel;
|
||||||
rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl,
|
rc = regmap_write(vreg->regmap,
|
||||||
vreg->spmi_dev->sid,
|
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
|
reg);
|
||||||
®, 1);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("%s: spmi_ext_register_writel failed, rc=%d\n",
|
pr_err("%s: regmap_write failed, rc=%d\n",
|
||||||
vreg->rdesc.name, rc);
|
vreg->rdesc.name, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -607,10 +614,13 @@ static int qpnp_smps_check_type(struct spm_vreg *vreg)
|
||||||
int rc;
|
int rc;
|
||||||
u8 type[2];
|
u8 type[2];
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_bulk_read(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_TYPE, type, 2);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_TYPE,
|
||||||
|
type,
|
||||||
|
2);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read type register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read type register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -627,7 +637,8 @@ static int qpnp_smps_check_type(struct spm_vreg *vreg)
|
||||||
&& type[1] == QPNP_HF_SUBTYPE) {
|
&& type[1] == QPNP_HF_SUBTYPE) {
|
||||||
vreg->regulator_type = QPNP_TYPE_HF;
|
vreg->regulator_type = QPNP_TYPE_HF;
|
||||||
} else {
|
} else {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: invalid type=0x%02X, subtype=0x%02X register pair\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: invalid type=0x%02X, subtype=0x%02X register pair\n",
|
||||||
__func__, type[0], type[1]);
|
__func__, type[0], type[1]);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
};
|
};
|
||||||
|
@ -640,21 +651,25 @@ static int qpnp_smps_init_range(struct spm_vreg *vreg,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg = 0;
|
u8 reg = 0;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_read(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_RANGE, ®, 1);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_RANGE,
|
||||||
|
&val);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read voltage range register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read voltage range register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
reg = (u8)val;
|
||||||
|
|
||||||
if (reg == 0x00) {
|
if (reg == 0x00) {
|
||||||
vreg->range = range0;
|
vreg->range = range0;
|
||||||
} else if (reg == 0x01) {
|
} else if (reg == 0x01) {
|
||||||
vreg->range = range1;
|
vreg->range = range1;
|
||||||
} else {
|
} else {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: voltage range=%d is invalid\n",
|
dev_err(&vreg->pdev->dev, "%s: voltage range=%d is invalid\n",
|
||||||
__func__, reg);
|
__func__, reg);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -666,14 +681,18 @@ static int qpnp_ult_hf_init_range(struct spm_vreg *vreg)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg = 0;
|
u8 reg = 0;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_read(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, ®, 1);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT,
|
||||||
|
&val);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read voltage range register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read voltage range register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
reg = (u8)val;
|
||||||
|
|
||||||
vreg->range = (reg < ULT_SMPS_RANGE_SPLIT) ? &ult_hf_range0 :
|
vreg->range = (reg < ULT_SMPS_RANGE_SPLIT) ? &ult_hf_range0 :
|
||||||
&ult_hf_range1;
|
&ult_hf_range1;
|
||||||
|
@ -709,8 +728,9 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg)
|
||||||
{
|
{
|
||||||
const char *mode_name;
|
const char *mode_name;
|
||||||
int rc;
|
int rc;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = of_property_read_string(vreg->spmi_dev->dev.of_node, "qcom,mode",
|
rc = of_property_read_string(vreg->pdev->dev.of_node, "qcom,mode",
|
||||||
&mode_name);
|
&mode_name);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (strcmp("pwm", mode_name) == 0) {
|
if (strcmp("pwm", mode_name) == 0) {
|
||||||
|
@ -719,26 +739,28 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg)
|
||||||
(vreg->regulator_type != QPNP_TYPE_ULT_HF)) {
|
(vreg->regulator_type != QPNP_TYPE_ULT_HF)) {
|
||||||
vreg->init_mode = QPNP_SMPS_MODE_AUTO;
|
vreg->init_mode = QPNP_SMPS_MODE_AUTO;
|
||||||
} else {
|
} else {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: unknown regulator mode: %s\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: unknown regulator mode: %s\n",
|
||||||
__func__, mode_name);
|
__func__, mode_name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(vreg->spmi_dev->ctrl,
|
rc = regmap_write(vreg->regmap,
|
||||||
vreg->spmi_dev->sid,
|
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
|
*&vreg->init_mode);
|
||||||
&vreg->init_mode, 1);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not write mode register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not write mode register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
} else {
|
} else {
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl,
|
rc = regmap_read(vreg->regmap,
|
||||||
vreg->spmi_dev->sid,
|
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_MODE,
|
&val);
|
||||||
&vreg->init_mode, 1);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read mode register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read mode register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
|
vreg->init_mode = (u8)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
vreg->mode = vreg->init_mode;
|
vreg->mode = vreg->init_mode;
|
||||||
|
@ -751,14 +773,17 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg)
|
||||||
int rc;
|
int rc;
|
||||||
u8 reg = 0;
|
u8 reg = 0;
|
||||||
int step = 0, delay;
|
int step = 0, delay;
|
||||||
|
uint val;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(vreg->spmi_dev->ctrl, vreg->spmi_dev->sid,
|
rc = regmap_read(vreg->regmap,
|
||||||
vreg->spmi_base_addr + QPNP_SMPS_REG_STEP_CTRL, ®, 1);
|
vreg->spmi_base_addr + QPNP_SMPS_REG_STEP_CTRL, &val);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&vreg->spmi_dev->dev, "%s: could not read stepping control register, rc=%d\n",
|
dev_err(&vreg->pdev->dev,
|
||||||
|
"%s: could not read stepping control register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
reg = (u8)val;
|
||||||
|
|
||||||
/* ULT buck does not support steps */
|
/* ULT buck does not support steps */
|
||||||
if (vreg->regulator_type != QPNP_TYPE_ULT_HF)
|
if (vreg->regulator_type != QPNP_TYPE_ULT_HF)
|
||||||
|
@ -858,18 +883,18 @@ static int spm_regulator_avs_register(struct spm_vreg *vreg,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spm_regulator_probe(struct spmi_device *spmi)
|
static int spm_regulator_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct regulator_config reg_config = {};
|
struct regulator_config reg_config = {};
|
||||||
struct device_node *node = spmi->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct regulator_init_data *init_data;
|
struct regulator_init_data *init_data;
|
||||||
struct spm_vreg *vreg;
|
struct spm_vreg *vreg;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
bool bypass_spm;
|
bool bypass_spm;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
dev_err(&spmi->dev, "%s: device node missing\n", __func__);
|
dev_err(&pdev->dev, "%s: device node missing\n", __func__);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,27 +903,34 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
rc = msm_spm_probe_done();
|
rc = msm_spm_probe_done();
|
||||||
if (rc) {
|
if (rc) {
|
||||||
if (rc != -EPROBE_DEFER)
|
if (rc != -EPROBE_DEFER)
|
||||||
dev_err(&spmi->dev, "%s: spm unavailable, rc=%d\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: spm unavailable, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vreg = devm_kzalloc(&spmi->dev, sizeof(*vreg), GFP_KERNEL);
|
vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
|
||||||
if (!vreg) {
|
if (!vreg) {
|
||||||
pr_err("allocation failed.\n");
|
pr_err("allocation failed.\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
vreg->spmi_dev = spmi;
|
vreg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
vreg->bypass_spm = bypass_spm;
|
if (!vreg->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
|
||||||
if (!res) {
|
|
||||||
dev_err(&spmi->dev, "%s: node is missing base address\n",
|
|
||||||
__func__);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
vreg->spmi_base_addr = res->start;
|
vreg->pdev = pdev;
|
||||||
|
vreg->bypass_spm = bypass_spm;
|
||||||
|
|
||||||
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
|
pdev->dev.of_node->full_name, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
vreg->spmi_base_addr = base;
|
||||||
|
|
||||||
rc = qpnp_smps_check_type(vreg);
|
rc = qpnp_smps_check_type(vreg);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -906,10 +938,10 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
/* Specify CPU 0 as default in order to handle shared regulator case. */
|
/* Specify CPU 0 as default in order to handle shared regulator case. */
|
||||||
vreg->cpu_num = 0;
|
vreg->cpu_num = 0;
|
||||||
of_property_read_u32(vreg->spmi_dev->dev.of_node, "qcom,cpu-num",
|
of_property_read_u32(vreg->pdev->dev.of_node, "qcom,cpu-num",
|
||||||
&vreg->cpu_num);
|
&vreg->cpu_num);
|
||||||
|
|
||||||
of_property_read_u32(vreg->spmi_dev->dev.of_node, "qcom,recal-mask",
|
of_property_read_u32(vreg->pdev->dev.of_node, "qcom,recal-mask",
|
||||||
&vreg->recal_cluster_mask);
|
&vreg->recal_cluster_mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -940,9 +972,9 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
init_data = of_get_regulator_init_data(&spmi->dev, node);
|
init_data = of_get_regulator_init_data(&pdev->dev, node);
|
||||||
if (!init_data) {
|
if (!init_data) {
|
||||||
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
|
dev_err(&pdev->dev, "%s: unable to allocate memory\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -953,7 +985,7 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
|
= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
|
||||||
|
|
||||||
if (!init_data->constraints.name) {
|
if (!init_data->constraints.name) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing regulator name\n",
|
dev_err(&pdev->dev, "%s: node is missing regulator name\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -967,7 +999,7 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
/ vreg->range->step_uV + 1;
|
/ vreg->range->step_uV + 1;
|
||||||
|
|
||||||
vreg->max_step_uV = SPM_REGULATOR_MAX_STEP_UV;
|
vreg->max_step_uV = SPM_REGULATOR_MAX_STEP_UV;
|
||||||
of_property_read_u32(vreg->spmi_dev->dev.of_node,
|
of_property_read_u32(vreg->pdev->dev.of_node,
|
||||||
"qcom,max-voltage-step", &vreg->max_step_uV);
|
"qcom,max-voltage-step", &vreg->max_step_uV);
|
||||||
|
|
||||||
if (vreg->max_step_uV > SPM_REGULATOR_MAX_STEP_UV)
|
if (vreg->max_step_uV > SPM_REGULATOR_MAX_STEP_UV)
|
||||||
|
@ -977,7 +1009,7 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
pr_debug("%s: max single voltage step size=%u uV\n",
|
pr_debug("%s: max single voltage step size=%u uV\n",
|
||||||
vreg->rdesc.name, vreg->max_step_uV);
|
vreg->rdesc.name, vreg->max_step_uV);
|
||||||
|
|
||||||
reg_config.dev = &spmi->dev;
|
reg_config.dev = &pdev->dev;
|
||||||
reg_config.init_data = init_data;
|
reg_config.init_data = init_data;
|
||||||
reg_config.driver_data = vreg;
|
reg_config.driver_data = vreg;
|
||||||
reg_config.of_node = node;
|
reg_config.of_node = node;
|
||||||
|
@ -985,18 +1017,18 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
if (IS_ERR(vreg->rdev)) {
|
if (IS_ERR(vreg->rdev)) {
|
||||||
rc = PTR_ERR(vreg->rdev);
|
rc = PTR_ERR(vreg->rdev);
|
||||||
dev_err(&spmi->dev, "%s: regulator_register failed, rc=%d\n",
|
dev_err(&pdev->dev, "%s: regulator_register failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = spm_regulator_avs_register(vreg, &spmi->dev, node);
|
rc = spm_regulator_avs_register(vreg, &pdev->dev, node);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
regulator_unregister(vreg->rdev);
|
regulator_unregister(vreg->rdev);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, vreg);
|
dev_set_drvdata(&pdev->dev, vreg);
|
||||||
|
|
||||||
pr_info("name=%s, range=%s, voltage=%d uV, mode=%s, step rate=%d uV/us\n",
|
pr_info("name=%s, range=%s, voltage=%d uV, mode=%s, step rate=%d uV/us\n",
|
||||||
vreg->rdesc.name,
|
vreg->rdesc.name,
|
||||||
|
@ -1009,9 +1041,9 @@ static int spm_regulator_probe(struct spmi_device *spmi)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spm_regulator_remove(struct spmi_device *spmi)
|
static int spm_regulator_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spm_vreg *vreg = dev_get_drvdata(&spmi->dev);
|
struct spm_vreg *vreg = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
if (vreg->avs_rdev)
|
if (vreg->avs_rdev)
|
||||||
regulator_unregister(vreg->avs_rdev);
|
regulator_unregister(vreg->avs_rdev);
|
||||||
|
@ -1025,13 +1057,13 @@ static struct of_device_id spm_regulator_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id spm_regulator_id[] = {
|
static const struct platform_device_id spm_regulator_id[] = {
|
||||||
{ SPM_REGULATOR_DRIVER_NAME, 0 },
|
{ SPM_REGULATOR_DRIVER_NAME, 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spmi, spm_regulator_id);
|
MODULE_DEVICE_TABLE(spmi, spm_regulator_id);
|
||||||
|
|
||||||
static struct spmi_driver spm_regulator_driver = {
|
static struct platform_driver spm_regulator_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = SPM_REGULATOR_DRIVER_NAME,
|
.name = SPM_REGULATOR_DRIVER_NAME,
|
||||||
.of_match_table = spm_regulator_match_table,
|
.of_match_table = spm_regulator_match_table,
|
||||||
|
@ -1059,13 +1091,13 @@ int __init spm_regulator_init(void)
|
||||||
else
|
else
|
||||||
has_registered = true;
|
has_registered = true;
|
||||||
|
|
||||||
return spmi_driver_register(&spm_regulator_driver);
|
return platform_driver_register(&spm_regulator_driver);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(spm_regulator_init);
|
EXPORT_SYMBOL(spm_regulator_init);
|
||||||
|
|
||||||
static void __exit spm_regulator_exit(void)
|
static void __exit spm_regulator_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&spm_regulator_driver);
|
platform_driver_unregister(&spm_regulator_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
arch_initcall(spm_regulator_init);
|
arch_initcall(spm_regulator_init);
|
||||||
|
|
|
@ -1593,7 +1593,7 @@ config RTC_DRV_MOXART
|
||||||
|
|
||||||
config RTC_DRV_QPNP
|
config RTC_DRV_QPNP
|
||||||
tristate "Qualcomm QPNP PMIC RTC"
|
tristate "Qualcomm QPNP PMIC RTC"
|
||||||
depends on (SPMI || MSM_SPMI) && OF_SPMI && MSM_QPNP_INT
|
depends on SPMI && OF_SPMI && MSM_QPNP_INT
|
||||||
help
|
help
|
||||||
Say Y here if you want to support the Qualcomm QPNP PMIC RTC.
|
Say Y here if you want to support the Qualcomm QPNP PMIC RTC.
|
||||||
|
|
||||||
|
@ -1610,6 +1610,15 @@ config RTC_DRV_MT6397
|
||||||
|
|
||||||
If you want to use Mediatek(R) RTC interface, select Y or M here.
|
If you want to use Mediatek(R) RTC interface, select Y or M here.
|
||||||
|
|
||||||
|
config RTC_DRV_QPNP
|
||||||
|
tristate "Qualcomm QPNP PMIC RTC"
|
||||||
|
depends on SPMI
|
||||||
|
help
|
||||||
|
Say Y here if you want to support the Qualcomm QPNP PMIC RTC.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called qpnp-rtc.
|
||||||
|
|
||||||
config RTC_DRV_XGENE
|
config RTC_DRV_XGENE
|
||||||
tristate "APM X-Gene RTC"
|
tristate "APM X-Gene RTC"
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
|
|
|
@ -11,15 +11,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/spmi.h>
|
|
||||||
#include <linux/alarmtimer.h>
|
#include <linux/alarmtimer.h>
|
||||||
|
|
||||||
/* RTC/ALARM Register offsets */
|
/* RTC/ALARM Register offsets */
|
||||||
|
@ -54,27 +56,26 @@ EXPORT_SYMBOL(poweron_alarm);
|
||||||
|
|
||||||
/* rtc driver internal structure */
|
/* rtc driver internal structure */
|
||||||
struct qpnp_rtc {
|
struct qpnp_rtc {
|
||||||
u8 rtc_ctrl_reg;
|
u8 rtc_ctrl_reg;
|
||||||
u8 alarm_ctrl_reg1;
|
u8 alarm_ctrl_reg1;
|
||||||
u16 rtc_base;
|
u16 rtc_base;
|
||||||
u16 alarm_base;
|
u16 alarm_base;
|
||||||
u32 rtc_write_enable;
|
u32 rtc_write_enable;
|
||||||
u32 rtc_alarm_powerup;
|
u32 rtc_alarm_powerup;
|
||||||
int rtc_alarm_irq;
|
int rtc_alarm_irq;
|
||||||
struct device *rtc_dev;
|
struct device *rtc_dev;
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
spinlock_t alarm_ctrl_lock;
|
struct regmap *regmap;
|
||||||
|
spinlock_t alarm_ctrl_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int qpnp_read_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
|
static int qpnp_read_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
|
||||||
u16 base, int count)
|
u16 base, int count)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct spmi_device *spmi = rtc_dd->spmi;
|
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, rtc_val,
|
rc = regmap_bulk_read(rtc_dd->regmap, base, rtc_val, count);
|
||||||
count);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(rtc_dd->rtc_dev, "SPMI read failed\n");
|
dev_err(rtc_dd->rtc_dev, "SPMI read failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -86,10 +87,8 @@ static int qpnp_write_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
|
||||||
u16 base, int count)
|
u16 base, int count)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct spmi_device *spmi = rtc_dd->spmi;
|
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, rtc_val,
|
rc = regmap_bulk_write(rtc_dd->regmap, base, rtc_val, count);
|
||||||
count);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
|
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -464,34 +463,38 @@ rtc_alarm_handled:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_rtc_probe(struct spmi_device *spmi)
|
static int qpnp_rtc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 subtype;
|
u8 subtype;
|
||||||
struct qpnp_rtc *rtc_dd;
|
struct qpnp_rtc *rtc_dd;
|
||||||
struct resource *resource;
|
unsigned int base;
|
||||||
struct spmi_resource *spmi_resource;
|
struct device_node *child;
|
||||||
|
|
||||||
rtc_dd = devm_kzalloc(&spmi->dev, sizeof(*rtc_dd), GFP_KERNEL);
|
rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
|
||||||
if (rtc_dd == NULL) {
|
if (rtc_dd == NULL)
|
||||||
dev_err(&spmi->dev, "Unable to allocate memory!\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!rtc_dd->regmap) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the rtc write property */
|
/* Get the rtc write property */
|
||||||
rc = of_property_read_u32(spmi->dev.of_node, "qcom,qpnp-rtc-write",
|
rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
|
||||||
&rtc_dd->rtc_write_enable);
|
&rtc_dd->rtc_write_enable);
|
||||||
if (rc && rc != -EINVAL) {
|
if (rc && rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Error reading rtc_write_enable property %d\n", rc);
|
"Error reading rtc_write_enable property %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(spmi->dev.of_node,
|
rc = of_property_read_u32(pdev->dev.of_node,
|
||||||
"qcom,qpnp-rtc-alarm-pwrup",
|
"qcom,qpnp-rtc-alarm-pwrup",
|
||||||
&rtc_dd->rtc_alarm_powerup);
|
&rtc_dd->rtc_alarm_powerup);
|
||||||
if (rc && rc != -EINVAL) {
|
if (rc && rc != -EINVAL) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Error reading rtc_alarm_powerup property %d\n", rc);
|
"Error reading rtc_alarm_powerup property %d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -499,53 +502,49 @@ static int qpnp_rtc_probe(struct spmi_device *spmi)
|
||||||
/* Initialise spinlock to protect RTC control register */
|
/* Initialise spinlock to protect RTC control register */
|
||||||
spin_lock_init(&rtc_dd->alarm_ctrl_lock);
|
spin_lock_init(&rtc_dd->alarm_ctrl_lock);
|
||||||
|
|
||||||
rtc_dd->rtc_dev = &(spmi->dev);
|
rtc_dd->rtc_dev = &(pdev->dev);
|
||||||
rtc_dd->spmi = spmi;
|
rtc_dd->pdev = pdev;
|
||||||
|
|
||||||
|
|
||||||
|
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
|
||||||
|
pr_err("no child nodes\n");
|
||||||
|
rc = -ENXIO;
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get RTC/ALARM resources */
|
/* Get RTC/ALARM resources */
|
||||||
spmi_for_each_container_dev(spmi_resource, spmi) {
|
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||||
if (!spmi_resource) {
|
rc = of_property_read_u32(child, "reg", &base);
|
||||||
dev_err(&spmi->dev,
|
if (rc < 0) {
|
||||||
"%s: rtc_alarm: spmi resource absent!\n",
|
dev_err(&pdev->dev,
|
||||||
__func__);
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
rc = -ENXIO;
|
child->full_name, rc);
|
||||||
goto fail_rtc_enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource = spmi_get_resource(spmi, spmi_resource,
|
|
||||||
IORESOURCE_MEM, 0);
|
|
||||||
if (!(resource && resource->start)) {
|
|
||||||
dev_err(&spmi->dev,
|
|
||||||
"%s: node %s IO resource absent!\n",
|
|
||||||
__func__, spmi->dev.of_node->full_name);
|
|
||||||
rc = -ENXIO;
|
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_read_wrapper(rtc_dd, &subtype,
|
rc = qpnp_read_wrapper(rtc_dd, &subtype,
|
||||||
resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
|
base + REG_OFFSET_PERP_SUBTYPE, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev,
|
||||||
"Peripheral subtype read failed\n");
|
"Peripheral subtype read failed\n");
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case RTC_PERPH_SUBTYPE:
|
case RTC_PERPH_SUBTYPE:
|
||||||
rtc_dd->rtc_base = resource->start;
|
rtc_dd->rtc_base = base;
|
||||||
break;
|
break;
|
||||||
case ALARM_PERPH_SUBTYPE:
|
case ALARM_PERPH_SUBTYPE:
|
||||||
rtc_dd->alarm_base = resource->start;
|
rtc_dd->alarm_base = base;
|
||||||
rtc_dd->rtc_alarm_irq =
|
rtc_dd->rtc_alarm_irq = of_irq_get(child, 0);
|
||||||
spmi_get_irq(spmi, spmi_resource, 0);
|
|
||||||
if (rtc_dd->rtc_alarm_irq < 0) {
|
if (rtc_dd->rtc_alarm_irq < 0) {
|
||||||
dev_err(&spmi->dev, "ALARM IRQ absent\n");
|
dev_err(&pdev->dev, "ALARM IRQ absent\n");
|
||||||
rc = -ENXIO;
|
rc = -ENXIO;
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&spmi->dev, "Invalid peripheral subtype\n");
|
dev_err(&pdev->dev, "Invalid peripheral subtype\n");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
@ -554,22 +553,19 @@ static int qpnp_rtc_probe(struct spmi_device *spmi)
|
||||||
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
|
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
|
||||||
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
|
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev, "Read from RTC control reg failed\n");
|
||||||
"Read from RTC control reg failed\n");
|
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
|
if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
|
||||||
"RTC h/w disabled, rtc not registered\n");
|
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
|
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
|
||||||
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
|
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev,
|
dev_err(&pdev->dev, "Read from Alarm control reg failed\n");
|
||||||
"Read from Alarm control reg failed\n");
|
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
/* Enable abort enable feature */
|
/* Enable abort enable feature */
|
||||||
|
@ -577,20 +573,20 @@ static int qpnp_rtc_probe(struct spmi_device *spmi)
|
||||||
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
|
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
|
||||||
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
|
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "SPMI write failed!\n");
|
dev_err(&pdev->dev, "SPMI write failed!\n");
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtc_dd->rtc_write_enable == true)
|
if (rtc_dd->rtc_write_enable == true)
|
||||||
qpnp_rtc_ops.set_time = qpnp_rtc_set_time;
|
qpnp_rtc_ops.set_time = qpnp_rtc_set_time;
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, rtc_dd);
|
dev_set_drvdata(&pdev->dev, rtc_dd);
|
||||||
|
|
||||||
/* Register the RTC device */
|
/* Register the RTC device */
|
||||||
rtc_dd->rtc = rtc_device_register("qpnp_rtc", &spmi->dev,
|
rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
|
||||||
&qpnp_rtc_ops, THIS_MODULE);
|
&qpnp_rtc_ops, THIS_MODULE);
|
||||||
if (IS_ERR(rtc_dd->rtc)) {
|
if (IS_ERR(rtc_dd->rtc)) {
|
||||||
dev_err(&spmi->dev, "%s: RTC registration failed (%ld)\n",
|
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
|
||||||
__func__, PTR_ERR(rtc_dd->rtc));
|
__func__, PTR_ERR(rtc_dd->rtc));
|
||||||
rc = PTR_ERR(rtc_dd->rtc);
|
rc = PTR_ERR(rtc_dd->rtc);
|
||||||
goto fail_rtc_enable;
|
goto fail_rtc_enable;
|
||||||
|
@ -604,38 +600,38 @@ static int qpnp_rtc_probe(struct spmi_device *spmi)
|
||||||
qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
|
qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
|
||||||
"qpnp_rtc_alarm", rtc_dd);
|
"qpnp_rtc_alarm", rtc_dd);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "Request IRQ failed (%d)\n", rc);
|
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
|
||||||
goto fail_req_irq;
|
goto fail_req_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_init_wakeup(&spmi->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
enable_irq_wake(rtc_dd->rtc_alarm_irq);
|
enable_irq_wake(rtc_dd->rtc_alarm_irq);
|
||||||
|
|
||||||
dev_dbg(&spmi->dev, "Probe success !!\n");
|
dev_dbg(&pdev->dev, "Probe success !!\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_req_irq:
|
fail_req_irq:
|
||||||
rtc_device_unregister(rtc_dd->rtc);
|
rtc_device_unregister(rtc_dd->rtc);
|
||||||
fail_rtc_enable:
|
fail_rtc_enable:
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_rtc_remove(struct spmi_device *spmi)
|
static int qpnp_rtc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_rtc *rtc_dd = dev_get_drvdata(&spmi->dev);
|
struct qpnp_rtc *rtc_dd = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
device_init_wakeup(&spmi->dev, 0);
|
device_init_wakeup(&pdev->dev, 0);
|
||||||
free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
|
free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
|
||||||
rtc_device_unregister(rtc_dd->rtc);
|
rtc_device_unregister(rtc_dd->rtc);
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qpnp_rtc_shutdown(struct spmi_device *spmi)
|
static void qpnp_rtc_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
u8 value[4] = {0};
|
u8 value[4] = {0};
|
||||||
u8 reg;
|
u8 reg;
|
||||||
|
@ -644,11 +640,11 @@ static void qpnp_rtc_shutdown(struct spmi_device *spmi)
|
||||||
struct qpnp_rtc *rtc_dd;
|
struct qpnp_rtc *rtc_dd;
|
||||||
bool rtc_alarm_powerup;
|
bool rtc_alarm_powerup;
|
||||||
|
|
||||||
if (!spmi) {
|
if (!pdev) {
|
||||||
pr_err("qpnp-rtc: spmi device not found\n");
|
pr_err("qpnp-rtc: spmi device not found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rtc_dd = dev_get_drvdata(&spmi->dev);
|
rtc_dd = dev_get_drvdata(&pdev->dev);
|
||||||
if (!rtc_dd) {
|
if (!rtc_dd) {
|
||||||
pr_err("qpnp-rtc: rtc driver data not found\n");
|
pr_err("qpnp-rtc: rtc driver data not found\n");
|
||||||
return;
|
return;
|
||||||
|
@ -656,7 +652,7 @@ static void qpnp_rtc_shutdown(struct spmi_device *spmi)
|
||||||
rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
|
rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
|
||||||
if (!rtc_alarm_powerup && !poweron_alarm) {
|
if (!rtc_alarm_powerup && !poweron_alarm) {
|
||||||
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
|
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
|
||||||
dev_dbg(&spmi->dev, "Disabling alarm interrupts\n");
|
dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
|
||||||
|
|
||||||
/* Disable RTC alarms */
|
/* Disable RTC alarms */
|
||||||
reg = rtc_dd->alarm_ctrl_reg1;
|
reg = rtc_dd->alarm_ctrl_reg1;
|
||||||
|
@ -687,26 +683,26 @@ static struct of_device_id spmi_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_rtc_driver = {
|
static struct platform_driver qpnp_rtc_driver = {
|
||||||
.probe = qpnp_rtc_probe,
|
.probe = qpnp_rtc_probe,
|
||||||
.remove = qpnp_rtc_remove,
|
.remove = qpnp_rtc_remove,
|
||||||
.shutdown = qpnp_rtc_shutdown,
|
.shutdown = qpnp_rtc_shutdown,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom,qpnp-rtc",
|
.name = "qcom,qpnp-rtc",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = spmi_match_table,
|
.of_match_table = spmi_match_table,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init qpnp_rtc_init(void)
|
static int __init qpnp_rtc_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_rtc_driver);
|
return platform_driver_register(&qpnp_rtc_driver);
|
||||||
}
|
}
|
||||||
module_init(qpnp_rtc_init);
|
module_init(qpnp_rtc_init);
|
||||||
|
|
||||||
static void __exit qpnp_rtc_exit(void)
|
static void __exit qpnp_rtc_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_rtc_driver);
|
platform_driver_unregister(&qpnp_rtc_driver);
|
||||||
}
|
}
|
||||||
module_exit(qpnp_rtc_exit);
|
module_exit(qpnp_rtc_exit);
|
||||||
|
|
||||||
|
|
|
@ -369,8 +369,7 @@ config THERMAL_QPNP
|
||||||
tristate "Qualcomm Plug-and-Play PMIC Temperature Alarm"
|
tristate "Qualcomm Plug-and-Play PMIC Temperature Alarm"
|
||||||
depends on THERMAL
|
depends on THERMAL
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
depends on OF_SPMI
|
|
||||||
help
|
help
|
||||||
This enables a thermal Sysfs driver for Qualcomm plug-and-play (QPNP)
|
This enables a thermal Sysfs driver for Qualcomm plug-and-play (QPNP)
|
||||||
PMIC devices. It shows up in Sysfs as a thermal zone with multiple
|
PMIC devices. It shows up in Sysfs as a thermal zone with multiple
|
||||||
|
@ -384,7 +383,7 @@ config THERMAL_QPNP
|
||||||
config THERMAL_QPNP_ADC_TM
|
config THERMAL_QPNP_ADC_TM
|
||||||
tristate "Qualcomm 8974 Thermal Monitor ADC Driver"
|
tristate "Qualcomm 8974 Thermal Monitor ADC Driver"
|
||||||
depends on THERMAL
|
depends on THERMAL
|
||||||
depends on SPMI || MSM_SPMI
|
depends on SPMI
|
||||||
help
|
help
|
||||||
This enables the thermal Sysfs driver for the ADC thermal monitoring
|
This enables the thermal Sysfs driver for the ADC thermal monitoring
|
||||||
device. It shows up in Sysfs as a thermal zone with multiple trip points.
|
device. It shows up in Sysfs as a thermal zone with multiple trip points.
|
||||||
|
|
2675
drivers/thermal/qpnp-adc-tm.c
Normal file
2675
drivers/thermal/qpnp-adc-tm.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -19,8 +19,10 @@
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
@ -80,7 +82,8 @@ enum qpnp_tm_adc_type {
|
||||||
|
|
||||||
struct qpnp_tm_chip {
|
struct qpnp_tm_chip {
|
||||||
struct delayed_work irq_work;
|
struct delayed_work irq_work;
|
||||||
struct spmi_device *spmi_dev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
struct thermal_zone_device *tz_dev;
|
struct thermal_zone_device *tz_dev;
|
||||||
const char *tm_name;
|
const char *tm_name;
|
||||||
enum qpnp_tm_adc_type adc_type;
|
enum qpnp_tm_adc_type adc_type;
|
||||||
|
@ -109,12 +112,14 @@ static inline int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
|
rc = regmap_bulk_read(chip->regmap, chip->base_addr + addr, buf, len);
|
||||||
chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
|
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
__func__, chip->spmi_dev->sid, chip->base_addr + addr,
|
"%s: regmap_bulk_readl failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
||||||
|
__func__,
|
||||||
|
to_spmi_device(chip->pdev->dev.parent)->usid,
|
||||||
|
chip->base_addr + addr,
|
||||||
len, rc);
|
len, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -125,12 +130,14 @@ static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
|
rc = regmap_bulk_write(chip->regmap, chip->base_addr + addr, buf, len);
|
||||||
chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
|
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
__func__, chip->spmi_dev->sid, chip->base_addr + addr,
|
"%s: regmap_bulk_write failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
|
||||||
|
__func__,
|
||||||
|
to_spmi_device(chip->pdev->dev.parent)->usid,
|
||||||
|
chip->base_addr + addr,
|
||||||
len, rc);
|
len, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -165,7 +172,8 @@ static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
|
||||||
if (!rc)
|
if (!rc)
|
||||||
chip->temperature = adc_result.physical;
|
chip->temperature = adc_result.physical;
|
||||||
else
|
else
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: qpnp_vadc_read(%d) failed, rc=%d\n",
|
||||||
__func__, chip->adc_channel, rc);
|
__func__, chip->adc_channel, rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -256,7 +264,8 @@ static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
|
||||||
|
|
||||||
rc = qpnp_tm_update_temp(chip);
|
rc = qpnp_tm_update_temp(chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&chip->spmi_dev->dev, "%s: %s: adc read failed, rc = %d\n",
|
dev_err(&chip->pdev->dev,
|
||||||
|
"%s: %s: adc read failed, rc = %d\n",
|
||||||
__func__, chip->tm_name, rc);
|
__func__, chip->tm_name, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -464,10 +473,10 @@ static int qpnp_tm_init_reg(struct qpnp_tm_chip *chip)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_tm_probe(struct spmi_device *spmi)
|
static int qpnp_tm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
struct resource *res;
|
unsigned int base;
|
||||||
struct qpnp_tm_chip *chip;
|
struct qpnp_tm_chip *chip;
|
||||||
struct thermal_zone_device_ops *tz_ops;
|
struct thermal_zone_device_ops *tz_ops;
|
||||||
char *tm_name;
|
char *tm_name;
|
||||||
|
@ -475,53 +484,53 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
u8 raw_type[2], type, subtype;
|
u8 raw_type[2], type, subtype;
|
||||||
|
|
||||||
if (!spmi || !(&spmi->dev) || !spmi->dev.of_node) {
|
if (!pdev || !(&pdev->dev) || !pdev->dev.of_node) {
|
||||||
dev_err(&spmi->dev, "%s: device tree node not found\n",
|
dev_err(&pdev->dev, "%s: device tree node not found\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = spmi->dev.of_node;
|
node = pdev->dev.of_node;
|
||||||
|
|
||||||
chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
|
chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
|
||||||
if (!chip) {
|
if (!chip)
|
||||||
dev_err(&spmi->dev, "%s: Can't allocate qpnp_tm_chip\n",
|
|
||||||
__func__);
|
|
||||||
return -ENOMEM;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, chip);
|
dev_set_drvdata(&pdev->dev, chip);
|
||||||
|
|
||||||
res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
||||||
if (!res) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing base address\n",
|
dev_err(&pdev->dev,
|
||||||
__func__);
|
"Couldn't find reg in node = %s rc = %d\n",
|
||||||
rc = -EINVAL;
|
pdev->dev.of_node->full_name, rc);
|
||||||
goto free_chip;
|
goto free_chip;
|
||||||
}
|
}
|
||||||
chip->base_addr = res->start;
|
chip->base_addr = base;
|
||||||
chip->spmi_dev = spmi;
|
chip->pdev = pdev;
|
||||||
|
|
||||||
chip->irq = spmi_get_irq(spmi, NULL, 0);
|
chip->irq = platform_get_irq(pdev, 0);
|
||||||
if (chip->irq < 0) {
|
if (chip->irq < 0) {
|
||||||
rc = chip->irq;
|
rc = chip->irq;
|
||||||
dev_err(&spmi->dev, "%s: node is missing irq, rc=%d\n",
|
dev_err(&pdev->dev, "%s: node is missing irq, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto free_chip;
|
goto free_chip;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->tm_name = of_get_property(node, "label", NULL);
|
chip->tm_name = of_get_property(node, "label", NULL);
|
||||||
if (chip->tm_name == NULL) {
|
if (chip->tm_name == NULL) {
|
||||||
dev_err(&spmi->dev, "%s: node is missing label\n",
|
dev_err(&pdev->dev, "%s: node is missing label\n", __func__);
|
||||||
__func__);
|
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto free_chip;
|
goto free_chip;
|
||||||
}
|
}
|
||||||
|
|
||||||
tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
|
tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
|
||||||
if (tm_name == NULL) {
|
if (tm_name == NULL) {
|
||||||
dev_err(&spmi->dev, "%s: could not allocate memory for label\n",
|
|
||||||
__func__);
|
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto free_chip;
|
goto free_chip;
|
||||||
}
|
}
|
||||||
|
@ -533,18 +542,20 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
chip->thresh = THRESH_MAX + 1;
|
chip->thresh = THRESH_MAX + 1;
|
||||||
rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
|
rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
|
||||||
if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
|
if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
|
||||||
dev_err(&spmi->dev, "%s: invalid qcom,threshold-set=%u specified\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: invalid qcom,threshold-set=%u specified\n",
|
||||||
__func__, chip->thresh);
|
__func__, chip->thresh);
|
||||||
|
|
||||||
chip->adc_type = QPNP_TM_ADC_NONE;
|
chip->adc_type = QPNP_TM_ADC_NONE;
|
||||||
rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
|
rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
|
if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
|
||||||
dev_err(&spmi->dev, "%s: invalid qcom,channel-num=%d specified\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: invalid qcom,channel-num=%d specified\n",
|
||||||
__func__, chip->adc_channel);
|
__func__, chip->adc_channel);
|
||||||
} else {
|
} else {
|
||||||
chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
|
chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
|
||||||
chip->vadc_dev = qpnp_get_vadc(&spmi->dev,
|
chip->vadc_dev = qpnp_get_vadc(&pdev->dev,
|
||||||
"temp_alarm");
|
"temp_alarm");
|
||||||
if (IS_ERR(chip->vadc_dev)) {
|
if (IS_ERR(chip->vadc_dev)) {
|
||||||
rc = PTR_ERR(chip->vadc_dev);
|
rc = PTR_ERR(chip->vadc_dev);
|
||||||
|
@ -570,7 +581,8 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
|
rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: could not read type register, rc=%d\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: could not read type register, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +590,8 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
subtype = raw_type[1];
|
subtype = raw_type[1];
|
||||||
|
|
||||||
if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
|
if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
|
||||||
dev_err(&spmi->dev, "%s: invalid type=%02X or subtype=%02X register value\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: invalid type=%02X or subtype=%02X register value\n",
|
||||||
__func__, type, subtype);
|
__func__, type, subtype);
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
|
@ -586,7 +599,7 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
|
|
||||||
rc = qpnp_tm_init_reg(chip);
|
rc = qpnp_tm_init_reg(chip);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
|
dev_err(&pdev->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
}
|
}
|
||||||
|
@ -594,7 +607,8 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
if (chip->adc_type == QPNP_TM_ADC_NONE) {
|
if (chip->adc_type == QPNP_TM_ADC_NONE) {
|
||||||
rc = qpnp_tm_init_temp_no_adc(chip);
|
rc = qpnp_tm_init_temp_no_adc(chip);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
}
|
}
|
||||||
|
@ -604,7 +618,8 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
chip->mode = THERMAL_DEVICE_DISABLED;
|
chip->mode = THERMAL_DEVICE_DISABLED;
|
||||||
rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
|
rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&spmi->dev, "%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
|
||||||
__func__, rc);
|
__func__, rc);
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
}
|
}
|
||||||
|
@ -612,7 +627,8 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, 0, chip,
|
chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, 0, chip,
|
||||||
tz_ops, NULL, 0, 0);
|
tz_ops, NULL, 0, 0);
|
||||||
if (chip->tz_dev == NULL) {
|
if (chip->tz_dev == NULL) {
|
||||||
dev_err(&spmi->dev, "%s: thermal_zone_device_register() failed.\n",
|
dev_err(&pdev->dev,
|
||||||
|
"%s: thermal_zone_device_register() failed.\n",
|
||||||
__func__);
|
__func__);
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto err_cancel_work;
|
goto err_cancel_work;
|
||||||
|
@ -621,7 +637,7 @@ static int qpnp_tm_probe(struct spmi_device *spmi)
|
||||||
rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
|
rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
|
||||||
chip);
|
chip);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(&spmi->dev, "%s: request_irq(%d) failed: %d\n",
|
dev_err(&pdev->dev, "%s: request_irq(%d) failed: %d\n",
|
||||||
__func__, chip->irq, rc);
|
__func__, chip->irq, rc);
|
||||||
goto err_free_tz;
|
goto err_free_tz;
|
||||||
}
|
}
|
||||||
|
@ -634,16 +650,16 @@ err_cancel_work:
|
||||||
cancel_delayed_work_sync(&chip->irq_work);
|
cancel_delayed_work_sync(&chip->irq_work);
|
||||||
kfree(chip->tm_name);
|
kfree(chip->tm_name);
|
||||||
free_chip:
|
free_chip:
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
kfree(chip);
|
kfree(chip);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpnp_tm_remove(struct spmi_device *spmi)
|
static int qpnp_tm_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qpnp_tm_chip *chip = dev_get_drvdata(&spmi->dev);
|
struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
dev_set_drvdata(&spmi->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
thermal_zone_device_unregister(chip->tz_dev);
|
thermal_zone_device_unregister(chip->tz_dev);
|
||||||
kfree(chip->tm_name);
|
kfree(chip->tm_name);
|
||||||
qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
|
qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
|
||||||
|
@ -691,12 +707,12 @@ static struct of_device_id qpnp_tm_match_table[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spmi_device_id qpnp_tm_id[] = {
|
static const struct platform_device_id qpnp_tm_id[] = {
|
||||||
{ QPNP_TM_DRIVER_NAME, 0 },
|
{ QPNP_TM_DRIVER_NAME, 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spmi_driver qpnp_tm_driver = {
|
static struct platform_driver qpnp_tm_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = QPNP_TM_DRIVER_NAME,
|
.name = QPNP_TM_DRIVER_NAME,
|
||||||
.of_match_table = qpnp_tm_match_table,
|
.of_match_table = qpnp_tm_match_table,
|
||||||
|
@ -710,12 +726,12 @@ static struct spmi_driver qpnp_tm_driver = {
|
||||||
|
|
||||||
int __init qpnp_tm_init(void)
|
int __init qpnp_tm_init(void)
|
||||||
{
|
{
|
||||||
return spmi_driver_register(&qpnp_tm_driver);
|
return platform_driver_register(&qpnp_tm_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit qpnp_tm_exit(void)
|
static void __exit qpnp_tm_exit(void)
|
||||||
{
|
{
|
||||||
spmi_driver_unregister(&qpnp_tm_driver);
|
platform_driver_unregister(&qpnp_tm_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(qpnp_tm_init);
|
module_init(qpnp_tm_init);
|
||||||
|
|
|
@ -1213,7 +1213,8 @@ struct qpnp_iadc_result {
|
||||||
* @calib - Internal rsens calibration values for gain and offset.
|
* @calib - Internal rsens calibration values for gain and offset.
|
||||||
*/
|
*/
|
||||||
struct qpnp_adc_drv {
|
struct qpnp_adc_drv {
|
||||||
struct spmi_device *spmi;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
uint8_t slave;
|
uint8_t slave;
|
||||||
uint16_t offset;
|
uint16_t offset;
|
||||||
struct qpnp_adc_properties *adc_prop;
|
struct qpnp_adc_properties *adc_prop;
|
||||||
|
@ -1315,7 +1316,7 @@ int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *dev,
|
||||||
* @spmi: spmi ADC device.
|
* @spmi: spmi ADC device.
|
||||||
* @adc_qpnp: spmi device tree node structure
|
* @adc_qpnp: spmi device tree node structure
|
||||||
*/
|
*/
|
||||||
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
|
int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
|
||||||
struct qpnp_adc_drv *adc_qpnp);
|
struct qpnp_adc_drv *adc_qpnp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
445
sound/soc/codecs/msm8916-wcd-irq.c
Normal file
445
sound/soc/codecs/msm8916-wcd-irq.c
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
/* Copyright (c) 2015, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/pm_qos.h>
|
||||||
|
#include <soc/qcom/pm.h>
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include "msm8x16-wcd.h"
|
||||||
|
#include "msm8916-wcd-irq.h"
|
||||||
|
#include "msm8x16_wcd_registers.h"
|
||||||
|
|
||||||
|
#define MAX_NUM_IRQS 14
|
||||||
|
#define NUM_IRQ_REGS 2
|
||||||
|
#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700
|
||||||
|
|
||||||
|
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
|
||||||
|
#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
|
||||||
|
|
||||||
|
static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data);
|
||||||
|
|
||||||
|
char *irq_names[MAX_NUM_IRQS] = {
|
||||||
|
"spk_cnp_int",
|
||||||
|
"spk_clip_int",
|
||||||
|
"spk_ocp_int",
|
||||||
|
"ins_rem_det1",
|
||||||
|
"but_rel_det",
|
||||||
|
"but_press_det",
|
||||||
|
"ins_rem_det",
|
||||||
|
"mbhc_int",
|
||||||
|
"ear_ocp_int",
|
||||||
|
"hphr_ocp_int",
|
||||||
|
"hphl_ocp_det",
|
||||||
|
"ear_cnp_int",
|
||||||
|
"hphr_cnp_int",
|
||||||
|
"hphl_cnp_int"
|
||||||
|
};
|
||||||
|
|
||||||
|
int order[MAX_NUM_IRQS] = {
|
||||||
|
MSM8X16_WCD_IRQ_SPKR_CNP,
|
||||||
|
MSM8X16_WCD_IRQ_SPKR_CLIP,
|
||||||
|
MSM8X16_WCD_IRQ_SPKR_OCP,
|
||||||
|
MSM8X16_WCD_IRQ_MBHC_INSREM_DET1,
|
||||||
|
MSM8X16_WCD_IRQ_MBHC_RELEASE,
|
||||||
|
MSM8X16_WCD_IRQ_MBHC_PRESS,
|
||||||
|
MSM8X16_WCD_IRQ_MBHC_INSREM_DET,
|
||||||
|
MSM8X16_WCD_IRQ_MBHC_HS_DET,
|
||||||
|
MSM8X16_WCD_IRQ_EAR_OCP,
|
||||||
|
MSM8X16_WCD_IRQ_HPHR_OCP,
|
||||||
|
MSM8X16_WCD_IRQ_HPHL_OCP,
|
||||||
|
MSM8X16_WCD_IRQ_EAR_CNP,
|
||||||
|
MSM8X16_WCD_IRQ_HPHR_CNP,
|
||||||
|
MSM8X16_WCD_IRQ_HPHL_CNP,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum wcd9xxx_spmi_pm_state {
|
||||||
|
WCD9XXX_PM_SLEEPABLE,
|
||||||
|
WCD9XXX_PM_AWAKE,
|
||||||
|
WCD9XXX_PM_ASLEEP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wcd9xxx_spmi_map {
|
||||||
|
uint8_t handled[NUM_IRQ_REGS];
|
||||||
|
uint8_t mask[NUM_IRQ_REGS];
|
||||||
|
int linuxirq[MAX_NUM_IRQS];
|
||||||
|
irq_handler_t handler[MAX_NUM_IRQS];
|
||||||
|
struct spmi_device *spmi[NUM_IRQ_REGS];
|
||||||
|
struct snd_soc_codec *codec;
|
||||||
|
|
||||||
|
enum wcd9xxx_spmi_pm_state pm_state;
|
||||||
|
struct mutex pm_lock;
|
||||||
|
/* pm_wq notifies change of pm_state */
|
||||||
|
wait_queue_head_t pm_wq;
|
||||||
|
struct pm_qos_request pm_qos_req;
|
||||||
|
int wlock_holders;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wcd9xxx_spmi_map map;
|
||||||
|
|
||||||
|
void wcd9xxx_spmi_enable_irq(int irq)
|
||||||
|
{
|
||||||
|
pr_debug("%s: irqno =%d\n", __func__, irq);
|
||||||
|
if ((irq >= 0) && (irq <= 7)) {
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_DIGITAL_INT_EN_CLR,
|
||||||
|
(0x01 << irq), 0x00);
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_DIGITAL_INT_EN_SET,
|
||||||
|
(0x01 << irq), (0x01 << irq));
|
||||||
|
}
|
||||||
|
if ((irq > 7) && (irq <= 15)) {
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_ANALOG_INT_EN_CLR,
|
||||||
|
(0x01 << (irq - 8)), 0x00);
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_ANALOG_INT_EN_SET,
|
||||||
|
(0x01 << (irq - 8)), (0x01 << (irq - 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))))
|
||||||
|
return;
|
||||||
|
|
||||||
|
map.mask[BIT_BYTE(irq)] &=
|
||||||
|
~(BYTE_BIT_MASK(irq));
|
||||||
|
|
||||||
|
enable_irq(map.linuxirq[irq]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wcd9xxx_spmi_disable_irq(int irq)
|
||||||
|
{
|
||||||
|
pr_debug("%s: irqno =%d\n", __func__, irq);
|
||||||
|
if ((irq >= 0) && (irq <= 7)) {
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_DIGITAL_INT_EN_SET,
|
||||||
|
(0x01 << (irq)), 0x00);
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_DIGITAL_INT_EN_CLR,
|
||||||
|
(0x01 << irq), (0x01 << irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((irq > 7) && (irq <= 15)) {
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_ANALOG_INT_EN_SET,
|
||||||
|
(0x01 << (irq - 8)), 0x00);
|
||||||
|
snd_soc_update_bits(map.codec,
|
||||||
|
MSM8X16_WCD_A_ANALOG_INT_EN_CLR,
|
||||||
|
(0x01 << (irq - 8)), (0x01 << (irq - 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
map.mask[BIT_BYTE(irq)] |=
|
||||||
|
(BYTE_BIT_MASK(irq));
|
||||||
|
|
||||||
|
disable_irq_nosync(map.linuxirq[irq]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
|
||||||
|
const char *name, void *priv)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
|
||||||
|
map.linuxirq[irq] =
|
||||||
|
spmi_get_irq_byname(map.spmi[BIT_BYTE(irq)], NULL,
|
||||||
|
irq_names[irq]);
|
||||||
|
|
||||||
|
if (strcmp(name, "mbhc sw intr"))
|
||||||
|
irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
|
||||||
|
IRQF_ONESHOT;
|
||||||
|
else
|
||||||
|
irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
|
||||||
|
IRQF_ONESHOT | IRQF_NO_SUSPEND;
|
||||||
|
pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags);
|
||||||
|
|
||||||
|
rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev,
|
||||||
|
map.linuxirq[irq], NULL,
|
||||||
|
wcd9xxx_spmi_irq_handler,
|
||||||
|
irq_flags,
|
||||||
|
name, priv);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&map.spmi[BIT_BYTE(irq)]->dev,
|
||||||
|
"Can't request %d IRQ\n", irq);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev,
|
||||||
|
"irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]);
|
||||||
|
map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
|
||||||
|
map.handler[irq] = handler;
|
||||||
|
enable_irq_wake(map.linuxirq[irq]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wcd9xxx_spmi_free_irq(int irq, void *priv)
|
||||||
|
{
|
||||||
|
devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq],
|
||||||
|
priv);
|
||||||
|
map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_irq_bit(int linux_irq)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i < MAX_NUM_IRQS; i++)
|
||||||
|
if (map.linuxirq[i] == linux_irq)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_order_irq(int i)
|
||||||
|
{
|
||||||
|
return order[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data)
|
||||||
|
{
|
||||||
|
int irq, i, j;
|
||||||
|
unsigned long status[NUM_IRQ_REGS] = {0};
|
||||||
|
|
||||||
|
if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
|
||||||
|
pr_err("Failed to hold suspend\n");
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = get_irq_bit(linux_irq);
|
||||||
|
if (irq == MAX_NUM_IRQS)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
|
||||||
|
for (i = 0; i < NUM_IRQ_REGS; i++) {
|
||||||
|
status[i] |= snd_soc_read(map.codec,
|
||||||
|
BIT_BYTE(irq) * 0x100 +
|
||||||
|
MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS);
|
||||||
|
status[i] &= ~map.mask[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAX_NUM_IRQS; i++) {
|
||||||
|
j = get_order_irq(i);
|
||||||
|
if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) &&
|
||||||
|
((map.handled[BIT_BYTE(j)] &
|
||||||
|
BYTE_BIT_MASK(j)) == 0)) {
|
||||||
|
map.handler[j](irq, data);
|
||||||
|
map.handled[BIT_BYTE(j)] |=
|
||||||
|
BYTE_BIT_MASK(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
|
||||||
|
wcd9xxx_spmi_unlock_sleep();
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg(
|
||||||
|
enum wcd9xxx_spmi_pm_state o,
|
||||||
|
enum wcd9xxx_spmi_pm_state n)
|
||||||
|
{
|
||||||
|
enum wcd9xxx_spmi_pm_state old;
|
||||||
|
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
old = map.pm_state;
|
||||||
|
if (old == o)
|
||||||
|
map.pm_state = n;
|
||||||
|
pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg);
|
||||||
|
|
||||||
|
int wcd9xxx_spmi_suspend(pm_message_t pmesg)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_debug("%s: enter\n", __func__);
|
||||||
|
/*
|
||||||
|
* pm_qos_update_request() can be called after this suspend chain call
|
||||||
|
* started. thus suspend can be called while lock is being held
|
||||||
|
*/
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
if (map.pm_state == WCD9XXX_PM_SLEEPABLE) {
|
||||||
|
pr_debug("%s: suspending system, state %d, wlock %d\n",
|
||||||
|
__func__, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
map.pm_state = WCD9XXX_PM_ASLEEP;
|
||||||
|
} else if (map.pm_state == WCD9XXX_PM_AWAKE) {
|
||||||
|
/*
|
||||||
|
* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
|
||||||
|
* then set to WCD9XXX_PM_ASLEEP
|
||||||
|
*/
|
||||||
|
pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
|
||||||
|
__func__, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
if (!(wait_event_timeout(map.pm_wq,
|
||||||
|
wcd9xxx_spmi_pm_cmpxchg(
|
||||||
|
WCD9XXX_PM_SLEEPABLE,
|
||||||
|
WCD9XXX_PM_ASLEEP) ==
|
||||||
|
WCD9XXX_PM_SLEEPABLE,
|
||||||
|
HZ))) {
|
||||||
|
pr_debug("%s: suspend failed state %d, wlock %d\n",
|
||||||
|
__func__, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
ret = -EBUSY;
|
||||||
|
} else {
|
||||||
|
pr_debug("%s: done, state %d, wlock %d\n", __func__,
|
||||||
|
map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
}
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
} else if (map.pm_state == WCD9XXX_PM_ASLEEP) {
|
||||||
|
pr_warn("%s: system is already suspended, state %d, wlock %dn",
|
||||||
|
__func__, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
}
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(wcd9xxx_spmi_suspend);
|
||||||
|
|
||||||
|
int wcd9xxx_spmi_resume(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_debug("%s: enter\n", __func__);
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
if (map.pm_state == WCD9XXX_PM_ASLEEP) {
|
||||||
|
pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
|
||||||
|
map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
map.pm_state = WCD9XXX_PM_SLEEPABLE;
|
||||||
|
} else {
|
||||||
|
pr_warn("%s: system is already awake, state %d wlock %d\n",
|
||||||
|
__func__, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
}
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
wake_up_all(&map.pm_wq);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(wcd9xxx_spmi_resume);
|
||||||
|
|
||||||
|
bool wcd9xxx_spmi_lock_sleep(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* wcd9xxx_spmi_{lock/unlock}_sleep will be called by
|
||||||
|
* wcd9xxx_spmi_irq_thread
|
||||||
|
* and its subroutines only motly.
|
||||||
|
* but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and
|
||||||
|
* It can race with wcd9xxx_spmi_irq_thread.
|
||||||
|
* So need to embrace wlock_holders with mutex.
|
||||||
|
*/
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
if (map.wlock_holders++ == 0) {
|
||||||
|
pr_debug("%s: holding wake lock\n", __func__);
|
||||||
|
pm_qos_update_request(&map.pm_qos_req,
|
||||||
|
msm_cpuidle_get_deep_idle_latency());
|
||||||
|
pm_stay_awake(&map.spmi[0]->dev);
|
||||||
|
}
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
pr_debug("%s: wake lock counter %d\n", __func__,
|
||||||
|
map.wlock_holders);
|
||||||
|
pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
|
||||||
|
|
||||||
|
if (!wait_event_timeout(map.pm_wq,
|
||||||
|
((wcd9xxx_spmi_pm_cmpxchg(
|
||||||
|
WCD9XXX_PM_SLEEPABLE,
|
||||||
|
WCD9XXX_PM_AWAKE)) ==
|
||||||
|
WCD9XXX_PM_SLEEPABLE ||
|
||||||
|
(wcd9xxx_spmi_pm_cmpxchg(
|
||||||
|
WCD9XXX_PM_SLEEPABLE,
|
||||||
|
WCD9XXX_PM_AWAKE) ==
|
||||||
|
WCD9XXX_PM_AWAKE)),
|
||||||
|
msecs_to_jiffies(
|
||||||
|
WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
|
||||||
|
pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
|
||||||
|
__func__,
|
||||||
|
WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state,
|
||||||
|
map.wlock_holders);
|
||||||
|
wcd9xxx_spmi_unlock_sleep();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wake_up_all(&map.pm_wq);
|
||||||
|
pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep);
|
||||||
|
|
||||||
|
void wcd9xxx_spmi_unlock_sleep(void)
|
||||||
|
{
|
||||||
|
mutex_lock(&map.pm_lock);
|
||||||
|
if (--map.wlock_holders == 0) {
|
||||||
|
pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
|
||||||
|
__func__, map.pm_state, WCD9XXX_PM_SLEEPABLE);
|
||||||
|
/*
|
||||||
|
* if wcd9xxx_spmi_lock_sleep failed, pm_state would be still
|
||||||
|
* WCD9XXX_PM_ASLEEP, don't overwrite
|
||||||
|
*/
|
||||||
|
if (likely(map.pm_state == WCD9XXX_PM_AWAKE))
|
||||||
|
map.pm_state = WCD9XXX_PM_SLEEPABLE;
|
||||||
|
pm_qos_update_request(&map.pm_qos_req,
|
||||||
|
PM_QOS_DEFAULT_VALUE);
|
||||||
|
pm_relax(&map.spmi[0]->dev);
|
||||||
|
}
|
||||||
|
mutex_unlock(&map.pm_lock);
|
||||||
|
pr_debug("%s: wake lock counter %d\n", __func__,
|
||||||
|
map.wlock_holders);
|
||||||
|
pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
|
||||||
|
wake_up_all(&map.pm_wq);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep);
|
||||||
|
|
||||||
|
void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec)
|
||||||
|
{
|
||||||
|
map.codec = codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wcd9xxx_spmi_set_dev(struct platform_device *pdev, int i)
|
||||||
|
{
|
||||||
|
if (i < NUM_IRQ_REGS)
|
||||||
|
map.spmi[i] = pdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wcd9xxx_spmi_irq_init(void)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; i < MAX_NUM_IRQS; i++)
|
||||||
|
map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
|
||||||
|
mutex_init(&map.pm_lock);
|
||||||
|
map.wlock_holders = 0;
|
||||||
|
map.pm_state = WCD9XXX_PM_SLEEPABLE;
|
||||||
|
init_waitqueue_head(&map.pm_wq);
|
||||||
|
pm_qos_add_request(&map.pm_qos_req,
|
||||||
|
PM_QOS_CPU_DMA_LATENCY,
|
||||||
|
PM_QOS_DEFAULT_VALUE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
34
sound/soc/codecs/msm8916-wcd-irq.h
Normal file
34
sound/soc/codecs/msm8916-wcd-irq.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/* Copyright (c) 2015, 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.
|
||||||
|
*/
|
||||||
|
#ifndef __WCD9XXX_SPMI_IRQ_H__
|
||||||
|
#define __WCD9XXX_SPMI_IRQ_H__
|
||||||
|
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include <linux/spmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/pm_qos.h>
|
||||||
|
|
||||||
|
extern void wcd9xxx_spmi_enable_irq(int irq);
|
||||||
|
extern void wcd9xxx_spmi_disable_irq(int irq);
|
||||||
|
extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
|
||||||
|
const char *name, void *priv);
|
||||||
|
extern int wcd9xxx_spmi_free_irq(int irq, void *priv);
|
||||||
|
extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec);
|
||||||
|
extern void wcd9xxx_spmi_set_dev(struct spmi_device *spmi, int i);
|
||||||
|
extern int wcd9xxx_spmi_irq_init(void);
|
||||||
|
extern int wcd9xxx_spmi_suspend(pm_message_t);
|
||||||
|
extern int wcd9xxx_spmi_resume(void);
|
||||||
|
bool wcd9xxx_spmi_lock_sleep(void);
|
||||||
|
void wcd9xxx_spmi_unlock_sleep(void);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue