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:
Abhijeet Dharmapurikar 2016-01-22 15:30:57 -08:00 committed by Rohit Vaswani
parent 065fd87ad3
commit 0c98f90e2e
46 changed files with 7620 additions and 2394 deletions

View file

@ -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>;

View 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";
};

View 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>;
};
};

View 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>;
};
};

View 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";
};
};

View 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";
};
};

View file

@ -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;
};
};
};

View 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>;
};
};
};

View file

@ -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>;

View file

@ -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

View file

@ -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

View file

@ -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, &reg, 1); rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, &reg, 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, &reg, 1); rc = qpnp_bsi_write(chip, QPNP_BSI_REG_CLEAR_ERROR, &reg, 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, &reg, 1); rc = qpnp_bsi_read(chip, QPNP_BSI_REG_BSI_ERROR, &reg, 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, &reg, 1); rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, &reg, 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, &reg, 1); rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, &reg, 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, &reg, 1); rc = qpnp_bsi_read(chip, QPNP_BSI_REG_STATUS, &reg, 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, &reg, 1); rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, &reg, 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, &reg, 1); rc = qpnp_bsi_write(chip, QPNP_BSI_REG_ENABLE, &reg, 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, &reg, 1); rc = qpnp_bsi_write(chip, QPNP_BSI_REG_TAU_CONFIG, &reg, 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, &reg, 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");

View file

@ -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

View file

@ -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");

View file

@ -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.

View file

@ -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");

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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, &reg, 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, &reg, 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, &reg, 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");

View file

@ -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, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base)); rc = qpnp_hap_read_reg(hap, &reg, 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

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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, &reg, 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, &reg, 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);

View file

@ -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, &reg, 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, &reg, 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);

View file

@ -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

View file

@ -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");

View file

@ -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

View file

@ -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, &reg, 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, &reg, 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);

View file

@ -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, &reg_config); vreg->rdev = regulator_register(rdesc, &reg_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");

View file

@ -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, &reg, 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);
&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, &reg, 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, &reg, 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, &reg, 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);

View file

@ -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

View file

@ -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);

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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);
/** /**

View 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");

View 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