mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 15:04:45 +00:00
- New Drivers
- Freescale MXS Low Resolution ADC - Freescale i.MX23/i.MX28 LRADC touchscreen - Motorola CPCAP Power Button - TI LMU (Lighting Management Unit) - Atmel SMC (Static Memory Controller) - New Device Support - Add support for X-Powers AXP803 to axp20x - Add support for Dialog Semi DA9061 to da9062-core - Add support for Intel Cougar Mountain to lpc_ich - Add support for Intel Gemini Lake to lpc_ich - New Functionality - Add Device Tree support; wm831x-*, axp20x, ti-lmu, da9062, sun4i-gpadc - Add IRQ sense support; motorola-cpcap - Add ACPI support; cros_ec - Add Reset support; altera-a10sr - Add ADC support; axp20x - Add AC Power support; axp20x - Add Runtime PM support; atmel-ebi, exynos-lpass - Add Battery Power Supply support; axp20x - Add Clock support; exynos-lpass, hi655x-pmic - Fix-ups - Implicitly specify required headers; motorola-cpcap, intel_soc_pmic_bxtwc - Add .remove() method; stm32-timers, exynos-lpass - Remove unused code; intel_soc_pmic_core, intel-lpss-acpi, ipaq-micro, atmel-smc, menelaus - Rename variables for clarity; axp20x - Convert pr_warning() to pr_warn(); db8500-prcmu, sta2x11-mfd, twl4030-power - Improve formatting; arizona-core, axp20x - Use raw_spinlock_*() variants; asic3, t7l66xb, tc6393xb - Simplify/refactor code; arizona-core, atmel-ebi - Improve error checking; intel_soc_pmic_core - Bug Fixes - Ensure OMAP3630/3730 boards can successfully reboot; twl4030-power - Correct max-register value; stm32-timers - Extend timeout to account for clock stretching; cros_ec_spi - Use correct IRQ trigger type; motorola-cpcap - Fix bad use of IRQ sense register; motorola-cpcap - Logic error "||" should be "&&"; mxs-lradc-ts -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJZAdDwAAoJEFGvii+H/HdhViMQAJ7Of3xKiS/P1d7RiOhs2OMY 41R4GojoY2QSurndIbV/PBUbNMlJiqvIawbFCBz7rAZnIv6NatFQGCQnATci8iDV tFxz2m705ifstSQTWUr2ykRdNUdKkShLPHdbjs0ZbpV6Xa5tIXT0U7WpdDr+J51B 422JHx8tVFrktkYCjg7VASKU9hzz8iRSbdpfu6ZitTT3yrr5Ivl0gaCCmXVyWTsF fy8DFvEpsAS1pToXGGeZHueTDIgePyEjwT+By6TuDvkObxvCbVrdhKrJnORfHRKx +aidbb4E8/ZNYmRERwl4VkAR7y2tenQat/Si+4rtwYHNTcapjjpdEElQTKkIAUpy L5Y9Ai0/ihDXpCPmMnf7omnt3qxAltE4voUk2WUIxDOiaFl6XwyxFPDoy5l8T2IM i1akRFss/lov9r3dWzxApTdMNwEdeXnHbZgW60h6RHyCH3dqfN3dFcfu9IX/ua01 HHI4ltkmaokXJmwvpa+/oVxGAfcoS5AGRw1uRfIN1fbjIxEeRS4I8iogqneVQ5GJ D766JIhuf1KKBIWu5DYwfCyTgSdBnEt/J/vTIe4zOZrBk/StbeygWfhUMRSutglK eORpwzsX8DnS4SYRErCcRRlePB/NU2GvmHOXSApSem9ifHx8sQGM7QZt2am5JYRp q/6gViepBHxrA8Xv6mWJ =SHiw -----END PGP SIGNATURE----- Merge tag 'mfd-next-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd Pull MFD updates from Lee Jones: "New Drivers: - Freescale MXS Low Resolution ADC - Freescale i.MX23/i.MX28 LRADC touchscreen - Motorola CPCAP Power Button - TI LMU (Lighting Management Unit) - Atmel SMC (Static Memory Controller) New Device Support: - Add support for X-Powers AXP803 to axp20x - Add support for Dialog Semi DA9061 to da9062-core - Add support for Intel Cougar Mountain to lpc_ich - Add support for Intel Gemini Lake to lpc_ich New Functionality: - Add Device Tree support; wm831x-*, axp20x, ti-lmu, da9062, sun4i-gpadc - Add IRQ sense support; motorola-cpcap - Add ACPI support; cros_ec - Add Reset support; altera-a10sr - Add ADC support; axp20x - Add AC Power support; axp20x - Add Runtime PM support; atmel-ebi, exynos-lpass - Add Battery Power Supply support; axp20x - Add Clock support; exynos-lpass, hi655x-pmic Fix-ups: - Implicitly specify required headers; motorola-cpcap, intel_soc_pmic_bxtwc - Add .remove() method; stm32-timers, exynos-lpass - Remove unused code; intel_soc_pmic_core, intel-lpss-acpi, ipaq-micro, atmel-smc, menelaus - Rename variables for clarity; axp20x - Convert pr_warning() to pr_warn(); db8500-prcmu, sta2x11-mfd, twl4030-power - Improve formatting; arizona-core, axp20x - Use raw_spinlock_*() variants; asic3, t7l66xb, tc6393xb - Simplify/refactor code; arizona-core, atmel-ebi - Improve error checking; intel_soc_pmic_core Bug Fixes: - Ensure OMAP3630/3730 boards can successfully reboot; twl4030-power - Correct max-register value; stm32-timers - Extend timeout to account for clock stretching; cros_ec_spi - Use correct IRQ trigger type; motorola-cpcap - Fix bad use of IRQ sense register; motorola-cpcap - Logic error "||" should be "&&"; mxs-lradc-ts" * tag 'mfd-next-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (79 commits) input: touchscreen: mxs-lradc: || vs && typos dt-bindings: Add AXP803's regulator info mfd: axp20x: Support AXP803 variant dt-bindings: Add device tree binding for X-Powers AXP803 PMIC dt-bindings: Make AXP20X compatible strings one per line mfd: intel_soc_pmic_core: Fix unchecked return value mfd: menelaus: Remove obsolete local_irq_disable() and local_irq_enable() mfd: omap-usb-tll: Configure ULPIAUTOIDLE mfd: omap-usb-tll: Fix inverted bit use for USB TLL mode mfd: palmas: Fixed spelling mistake in error message mfd: lpc_ich: Add support for Intel Gemini Lake SoC mfd: hi655x: Add the clock cell to provide WiFi and Bluetooth mfd: intel_soc_pmic: Fix a mess with compilation units mfd: exynos-lpass: Add runtime PM support mfd: exynos-lpass: Add missing remove() function mfd: exynos-lpass: Add support for clocks mfd: exynos-lpass: Remove pad retention control iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs mfd: cpcap: Fix bad use of IRQ sense register mfd: cpcap: Use ack_invert interrupts ...
This commit is contained in:
commit
d26f552ebb
20
Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
Normal file
20
Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Motorola CPCAP on key
|
||||
|
||||
This module is part of the CPCAP. For more details about the whole
|
||||
chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
|
||||
|
||||
This module provides a simple power button event via an Interrupt.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following
|
||||
- "motorola,cpcap-pwrbutton"
|
||||
- interrupts: irq specifier for CPCAP's ON IRQ
|
||||
|
||||
Example:
|
||||
|
||||
&cpcap {
|
||||
cpcap_pwrbutton: pwrbutton {
|
||||
compatible = "motorola,cpcap-pwrbutton";
|
||||
interrupts = <23 IRQ_TYPE_NONE>;
|
||||
};
|
||||
};
|
@ -18,6 +18,7 @@ The A10SR consists of these sub-devices:
|
||||
Device Description
|
||||
------ ----------
|
||||
a10sr_gpio GPIO Controller
|
||||
a10sr_rst Reset Controller
|
||||
|
||||
Arria10 GPIO
|
||||
Required Properties:
|
||||
@ -27,6 +28,11 @@ Required Properties:
|
||||
the second cell is used to specify flags.
|
||||
See ../gpio/gpio.txt for more information.
|
||||
|
||||
Arria10 Peripheral PHY Reset
|
||||
Required Properties:
|
||||
- compatible : Should be "altr,a10sr-reset"
|
||||
- #reset-cells : Should be one.
|
||||
|
||||
Example:
|
||||
|
||||
resource-manager@0 {
|
||||
@ -43,4 +49,9 @@ Example:
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
a10sr_rst: reset-controller {
|
||||
compatible = "altr,a10sr-reset";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
@ -6,12 +6,19 @@ axp202 (X-Powers)
|
||||
axp209 (X-Powers)
|
||||
axp221 (X-Powers)
|
||||
axp223 (X-Powers)
|
||||
axp803 (X-Powers)
|
||||
axp809 (X-Powers)
|
||||
|
||||
Required properties:
|
||||
- compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209",
|
||||
"x-powers,axp221", "x-powers,axp223", "x-powers,axp806",
|
||||
"x-powers,axp809"
|
||||
- compatible: should be one of:
|
||||
* "x-powers,axp152"
|
||||
* "x-powers,axp202"
|
||||
* "x-powers,axp209"
|
||||
* "x-powers,axp221"
|
||||
* "x-powers,axp223"
|
||||
* "x-powers,axp803"
|
||||
* "x-powers,axp806"
|
||||
* "x-powers,axp809"
|
||||
- reg: The I2C slave address or RSB hardware address for the AXP chip
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
|
||||
@ -28,6 +35,9 @@ Optional properties:
|
||||
regulator to drive the OTG VBus, rather then as an input pin
|
||||
which signals whether the board is driving OTG VBus or not.
|
||||
|
||||
- x-powers,master-mode: Boolean (axp806 only). Set this when the PMIC is
|
||||
wired for master mode. The default is slave mode.
|
||||
|
||||
- <input>-supply: a phandle to the regulator supply node. May be omitted if
|
||||
inputs are unregulated, such as using the IPSOUT output
|
||||
from the PMIC.
|
||||
@ -86,6 +96,33 @@ LDO_IO1 : LDO : ips-supply : GPIO 1
|
||||
RTC_LDO : LDO : ips-supply : always on
|
||||
DRIVEVBUS : Enable output : drivevbus-supply : external regulator
|
||||
|
||||
AXP803 regulators, type, and corresponding input supply names:
|
||||
|
||||
Regulator Type Supply Name Notes
|
||||
--------- ---- ----------- -----
|
||||
DCDC1 : DC-DC buck : vin1-supply
|
||||
DCDC2 : DC-DC buck : vin2-supply : poly-phase capable
|
||||
DCDC3 : DC-DC buck : vin3-supply : poly-phase capable
|
||||
DCDC4 : DC-DC buck : vin4-supply
|
||||
DCDC5 : DC-DC buck : vin5-supply : poly-phase capable
|
||||
DCDC6 : DC-DC buck : vin6-supply : poly-phase capable
|
||||
DC1SW : On/Off Switch : : DCDC1 secondary output
|
||||
ALDO1 : LDO : aldoin-supply : shared supply
|
||||
ALDO2 : LDO : aldoin-supply : shared supply
|
||||
ALDO3 : LDO : aldoin-supply : shared supply
|
||||
DLDO1 : LDO : dldoin-supply : shared supply
|
||||
DLDO2 : LDO : dldoin-supply : shared supply
|
||||
DLDO3 : LDO : dldoin-supply : shared supply
|
||||
DLDO4 : LDO : dldoin-supply : shared supply
|
||||
ELDO1 : LDO : eldoin-supply : shared supply
|
||||
ELDO2 : LDO : eldoin-supply : shared supply
|
||||
ELDO3 : LDO : eldoin-supply : shared supply
|
||||
FLDO1 : LDO : fldoin-supply : shared supply
|
||||
FLDO2 : LDO : fldoin-supply : shared supply
|
||||
LDO_IO0 : LDO : ips-supply : GPIO 0
|
||||
LDO_IO1 : LDO : ips-supply : GPIO 1
|
||||
RTC_LDO : LDO : ips-supply : always on
|
||||
|
||||
AXP806 regulators, type, and corresponding input supply names:
|
||||
|
||||
Regulator Type Supply Name Notes
|
||||
|
@ -1,22 +1,39 @@
|
||||
* Dialog DA9062 Power Management Integrated Circuit (PMIC)
|
||||
|
||||
DA9062 consists of a large and varied group of sub-devices:
|
||||
Product information for the DA9062 and DA9061 devices can be found here:
|
||||
- http://www.dialog-semiconductor.com/products/da9062
|
||||
- http://www.dialog-semiconductor.com/products/da9061
|
||||
|
||||
The DA9062 PMIC consists of:
|
||||
|
||||
Device Supply Names Description
|
||||
------ ------------ -----------
|
||||
da9062-regulator : : LDOs & BUCKs
|
||||
da9062-rtc : : Real-Time Clock
|
||||
da9062-onkey : : On Key
|
||||
da9062-watchdog : : Watchdog Timer
|
||||
da9062-thermal : : Thermal
|
||||
|
||||
The DA9061 PMIC consists of:
|
||||
|
||||
Device Supply Names Description
|
||||
------ ------------ -----------
|
||||
da9062-regulator : : LDOs & BUCKs
|
||||
da9062-onkey : : On Key
|
||||
da9062-watchdog : : Watchdog Timer
|
||||
da9062-thermal : : Thermal
|
||||
|
||||
======
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "dlg,da9062".
|
||||
- compatible : Should be
|
||||
"dlg,da9062" for DA9062
|
||||
"dlg,da9061" for DA9061
|
||||
- reg : Specifies the I2C slave address (this defaults to 0x58 but it can be
|
||||
modified to match the chip's OTP settings).
|
||||
- interrupt-parent : Specifies the reference to the interrupt controller for
|
||||
the DA9062.
|
||||
the DA9062 or DA9061.
|
||||
- interrupts : IRQ line information.
|
||||
- interrupt-controller
|
||||
|
||||
@ -25,8 +42,8 @@ further information on IRQ bindings.
|
||||
|
||||
Sub-nodes:
|
||||
|
||||
- regulators : This node defines the settings for the LDOs and BUCKs. The
|
||||
DA9062 regulators are bound using their names listed below:
|
||||
- regulators : This node defines the settings for the LDOs and BUCKs.
|
||||
The DA9062 regulators are bound using their names listed below:
|
||||
|
||||
buck1 : BUCK_1
|
||||
buck2 : BUCK_2
|
||||
@ -37,19 +54,29 @@ Sub-nodes:
|
||||
ldo3 : LDO_3
|
||||
ldo4 : LDO_4
|
||||
|
||||
The DA9061 regulators are bound using their names listed below:
|
||||
|
||||
buck1 : BUCK_1
|
||||
buck2 : BUCK_2
|
||||
buck3 : BUCK_3
|
||||
ldo1 : LDO_1
|
||||
ldo2 : LDO_2
|
||||
ldo3 : LDO_3
|
||||
ldo4 : LDO_4
|
||||
|
||||
The component follows the standard regulator framework and the bindings
|
||||
details of individual regulator device can be found in:
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
|
||||
- rtc : This node defines settings required for the Real-Time Clock associated
|
||||
with the DA9062. There are currently no entries in this binding, however
|
||||
compatible = "dlg,da9062-rtc" should be added if a node is created.
|
||||
|
||||
- watchdog: This node defines the settings for the watchdog driver associated
|
||||
with the DA9062 PMIC. The compatible = "dlg,da9062-watchdog" should be added
|
||||
if a node is created.
|
||||
- onkey : See ../input/da9062-onkey.txt
|
||||
|
||||
- watchdog: See ../watchdog/da9062-watchdog.txt
|
||||
|
||||
- thermal : See ../thermal/da9062-thermal.txt
|
||||
|
||||
Example:
|
||||
|
||||
@ -64,10 +91,6 @@ Example:
|
||||
compatible = "dlg,da9062-rtc";
|
||||
};
|
||||
|
||||
watchdog {
|
||||
compatible = "dlg,da9062-watchdog";
|
||||
};
|
||||
|
||||
regulators {
|
||||
DA9062_BUCK1: buck1 {
|
||||
regulator-name = "BUCK1";
|
||||
|
@ -6,6 +6,7 @@ MT6397/MT6323 is a multifunction device with the following sub modules:
|
||||
- Audio codec
|
||||
- GPIO
|
||||
- Clock
|
||||
- LED
|
||||
|
||||
It is interfaced to host controller using SPI interface by a proprietary hardware
|
||||
called PMIC wrapper or pwrap. MT6397/MT6323 MFD is a child device of pwrap.
|
||||
|
@ -5,7 +5,10 @@ Required properties:
|
||||
- compatible : "samsung,exynos5433-lpass"
|
||||
- reg : should contain the LPASS top SFR region location
|
||||
and size
|
||||
- samsung,pmu-syscon : the phandle to the Power Management Unit node
|
||||
- clock-names : should contain following required clocks: "sfr0_ctrl"
|
||||
- clocks : should contain clock specifiers of all clocks, which
|
||||
input names have been specified in clock-names
|
||||
property, in same order.
|
||||
- #address-cells : should be 1
|
||||
- #size-cells : should be 1
|
||||
- ranges : must be present
|
||||
@ -25,7 +28,8 @@ Example:
|
||||
audio-subsystem {
|
||||
compatible = "samsung,exynos5433-lpass";
|
||||
reg = <0x11400000 0x100>, <0x11500000 0x08>;
|
||||
samsung,pmu-syscon = <&pmu_system_controller>;
|
||||
clocks = <&cmu_aud CLK_PCLK_SFR0_CTRL>;
|
||||
clock-names = "sfr0_ctrl";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
@ -1,3 +1,24 @@
|
||||
Allwinner SoCs' GPADC Device Tree bindings
|
||||
------------------------------------------
|
||||
The Allwinner SoCs all have an ADC that can also act as a thermal sensor
|
||||
and sometimes as a touchscreen controller.
|
||||
|
||||
Required properties:
|
||||
- compatible: "allwinner,sun8i-a33-ths",
|
||||
- reg: mmio address range of the chip,
|
||||
- #thermal-sensor-cells: shall be 0,
|
||||
- #io-channel-cells: shall be 0,
|
||||
|
||||
Example:
|
||||
ths: ths@01c25000 {
|
||||
compatible = "allwinner,sun8i-a33-ths";
|
||||
reg = <0x01c25000 0x100>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
#io-channel-cells = <0>;
|
||||
};
|
||||
|
||||
sun4i, sun5i and sun6i SoCs are also supported via the older binding:
|
||||
|
||||
sun4i resistive touchscreen controller
|
||||
--------------------------------------
|
||||
|
243
Documentation/devicetree/bindings/mfd/ti-lmu.txt
Normal file
243
Documentation/devicetree/bindings/mfd/ti-lmu.txt
Normal file
@ -0,0 +1,243 @@
|
||||
TI LMU (Lighting Management Unit) device tree bindings
|
||||
|
||||
TI LMU driver supports lighting devices below.
|
||||
|
||||
Name Child nodes
|
||||
------ ---------------------------------
|
||||
LM3532 Backlight
|
||||
LM3631 Backlight and regulator
|
||||
LM3632 Backlight and regulator
|
||||
LM3633 Backlight, LED and fault monitor
|
||||
LM3695 Backlight
|
||||
LM3697 Backlight and fault monitor
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
"ti,lm3532"
|
||||
"ti,lm3631"
|
||||
"ti,lm3632"
|
||||
"ti,lm3633"
|
||||
"ti,lm3695"
|
||||
"ti,lm3697"
|
||||
- reg: I2C slave address.
|
||||
0x11 for LM3632
|
||||
0x29 for LM3631
|
||||
0x36 for LM3633, LM3697
|
||||
0x38 for LM3532
|
||||
0x63 for LM3695
|
||||
|
||||
Optional property:
|
||||
- enable-gpios: A GPIO specifier for hardware enable pin.
|
||||
|
||||
Required node:
|
||||
- backlight: All LMU devices have backlight child nodes.
|
||||
For the properties, please refer to [1].
|
||||
|
||||
Optional nodes:
|
||||
- fault-monitor: Hardware fault monitoring driver for LM3633 and LM3697.
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
"ti,lm3633-fault-monitor"
|
||||
"ti,lm3697-fault-monitor"
|
||||
- leds: LED properties for LM3633. Please refer to [2].
|
||||
- regulators: Regulator properties for LM3631 and LM3632.
|
||||
Please refer to [3].
|
||||
|
||||
[1] ../leds/backlight/ti-lmu-backlight.txt
|
||||
[2] ../leds/leds-lm3633.txt
|
||||
[3] ../regulator/lm363x-regulator.txt
|
||||
|
||||
lm3532@38 {
|
||||
compatible = "ti,lm3532";
|
||||
reg = <0x38>;
|
||||
|
||||
enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3532-backlight";
|
||||
|
||||
lcd {
|
||||
led-sources = <0 1 2>;
|
||||
ramp-up-msec = <30>;
|
||||
ramp-down-msec = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lm3631@29 {
|
||||
compatible = "ti,lm3631";
|
||||
reg = <0x29>;
|
||||
|
||||
regulators {
|
||||
compatible = "ti,lm363x-regulator";
|
||||
|
||||
vboost {
|
||||
regulator-name = "lcd_boost";
|
||||
regulator-min-microvolt = <4500000>;
|
||||
regulator-max-microvolt = <6350000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vcont {
|
||||
regulator-name = "lcd_vcont";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
|
||||
voref {
|
||||
regulator-name = "lcd_voref";
|
||||
regulator-min-microvolt = <4000000>;
|
||||
regulator-max-microvolt = <6000000>;
|
||||
};
|
||||
|
||||
vpos {
|
||||
regulator-name = "lcd_vpos";
|
||||
regulator-min-microvolt = <4000000>;
|
||||
regulator-max-microvolt = <6000000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
vneg {
|
||||
regulator-name = "lcd_vneg";
|
||||
regulator-min-microvolt = <4000000>;
|
||||
regulator-max-microvolt = <6000000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3631-backlight";
|
||||
|
||||
lcd_bl {
|
||||
led-sources = <0 1>;
|
||||
ramp-up-msec = <300>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lm3632@11 {
|
||||
compatible = "ti,lm3632";
|
||||
reg = <0x11>;
|
||||
|
||||
enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; /* PC2 */
|
||||
|
||||
regulators {
|
||||
compatible = "ti,lm363x-regulator";
|
||||
|
||||
ti,lcm-en1-gpio = <&pioC 0 GPIO_ACTIVE_HIGH>; /* PC0 */
|
||||
ti,lcm-en2-gpio = <&pioC 1 GPIO_ACTIVE_HIGH>; /* PC1 */
|
||||
|
||||
vboost {
|
||||
regulator-name = "lcd_boost";
|
||||
regulator-min-microvolt = <4500000>;
|
||||
regulator-max-microvolt = <6400000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vpos {
|
||||
regulator-name = "lcd_vpos";
|
||||
regulator-min-microvolt = <4000000>;
|
||||
regulator-max-microvolt = <6000000>;
|
||||
};
|
||||
|
||||
vneg {
|
||||
regulator-name = "lcd_vneg";
|
||||
regulator-min-microvolt = <4000000>;
|
||||
regulator-max-microvolt = <6000000>;
|
||||
};
|
||||
};
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3632-backlight";
|
||||
|
||||
pwms = <&pwm0 0 10000 0>; /* pwm number, period, polarity */
|
||||
pwm-names = "lmu-backlight";
|
||||
|
||||
lcd {
|
||||
led-sources = <0 1>;
|
||||
pwm-period = <10000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lm3633@36 {
|
||||
compatible = "ti,lm3633";
|
||||
reg = <0x36>;
|
||||
|
||||
enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3633-backlight";
|
||||
|
||||
main {
|
||||
label = "main_lcd";
|
||||
led-sources = <1 2>;
|
||||
ramp-up-msec = <500>;
|
||||
ramp-down-msec = <500>;
|
||||
};
|
||||
|
||||
front {
|
||||
label = "front_lcd";
|
||||
led-sources = <0>;
|
||||
ramp-up-msec = <1000>;
|
||||
ramp-down-msec = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "ti,lm3633-leds";
|
||||
|
||||
chan1 {
|
||||
label = "status";
|
||||
led-sources = <1>;
|
||||
led-max-microamp = <6000>;
|
||||
};
|
||||
|
||||
chan345 {
|
||||
label = "rgb";
|
||||
led-sources = <3 4 5>;
|
||||
led-max-microamp = <10000>;
|
||||
};
|
||||
};
|
||||
|
||||
fault-monitor {
|
||||
compatible = "ti,lm3633-fault-monitor";
|
||||
};
|
||||
};
|
||||
|
||||
lm3695@63 {
|
||||
compatible = "ti,lm3695";
|
||||
reg = <0x63>;
|
||||
|
||||
enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3695-backlight";
|
||||
|
||||
lcd {
|
||||
label = "bl";
|
||||
led-sources = <0 1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lm3697@36 {
|
||||
compatible = "ti,lm3697";
|
||||
reg = <0x36>;
|
||||
|
||||
enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
backlight {
|
||||
compatible = "ti,lm3697-backlight";
|
||||
|
||||
lcd {
|
||||
led-sources = <0 1 2>;
|
||||
ramp-up-msec = <200>;
|
||||
ramp-down-msec = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
fault-monitor {
|
||||
compatible = "ti,lm3697-fault-monitor";
|
||||
};
|
||||
};
|
81
Documentation/devicetree/bindings/mfd/wm831x.txt
Normal file
81
Documentation/devicetree/bindings/mfd/wm831x.txt
Normal file
@ -0,0 +1,81 @@
|
||||
Cirrus Logic/Wolfson Microelectronics wm831x PMICs
|
||||
|
||||
System PMICs with a wide range of additional features.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of the following chip-specific strings:
|
||||
"wlf,wm8310"
|
||||
"wlf,wm8311"
|
||||
"wlf,wm8312"
|
||||
"wlf,wm8320"
|
||||
"wlf,wm8321"
|
||||
"wlf,wm8325"
|
||||
"wlf,wm8326"
|
||||
|
||||
- reg : I2C slave address when connected using I2C, chip select number
|
||||
when using SPI.
|
||||
|
||||
- gpio-controller : Indicates this device is a GPIO controller.
|
||||
- #gpio-cells : Must be 2. The first cell is the pin number and the
|
||||
second cell is used to specify optional parameters (currently unused).
|
||||
|
||||
- interrupts : The interrupt line the IRQ signal for the device is
|
||||
connected to.
|
||||
- interrupt-parent : The parent interrupt controller.
|
||||
|
||||
- interrupt-controller : wm831x devices contain interrupt controllers and
|
||||
may provide interrupt services to other devices.
|
||||
- #interrupt-cells: Must be 2. The first cell is the IRQ number, and the
|
||||
second cell is the flags, encoded as the trigger masks from
|
||||
../interrupt-controller/interrupts.txt
|
||||
|
||||
Optional sub-nodes:
|
||||
- regulators : Contains sub-nodes for each of the regulators supplied by
|
||||
the device. The regulators are bound using their names listed below:
|
||||
|
||||
dcdc1 : DCDC1
|
||||
dcdc2 : DCDC2
|
||||
dcdc3 : DCDC3
|
||||
dcdc4 : DCDC3
|
||||
isink1 : ISINK1
|
||||
isink2 : ISINK2
|
||||
ldo1 : LDO1
|
||||
ldo2 : LDO2
|
||||
ldo3 : LDO3
|
||||
ldo4 : LDO4
|
||||
ldo5 : LDO5
|
||||
ldo7 : LDO7
|
||||
ldo11 : LDO11
|
||||
|
||||
The bindings details of each regulator can be found in:
|
||||
../regulator/regulator.txt
|
||||
|
||||
Example:
|
||||
|
||||
wm8310: pmic@36 {
|
||||
compatible = "wlf,wm8310";
|
||||
reg = <0x36>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
interrupts = <347>;
|
||||
interrupt-parent = <&gic>;
|
||||
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
|
||||
regulators {
|
||||
dcdc1: dcdc1 {
|
||||
regulator-name = "DCDC1";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <600000>;
|
||||
};
|
||||
ldo1: ldo1 {
|
||||
regulator-name = "LDO1";
|
||||
regulator-min-microvolt = <1700000>;
|
||||
regulator-max-microvolt = <1700000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -13731,6 +13731,7 @@ F: Documentation/hwmon/wm83??
|
||||
F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt
|
||||
F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt
|
||||
F: Documentation/devicetree/bindings/mfd/arizona.txt
|
||||
F: Documentation/devicetree/bindings/mfd/wm831x.txt
|
||||
F: arch/arm/mach-s3c64xx/mach-crag6410*
|
||||
F: drivers/clk/clk-wm83*.c
|
||||
F: drivers/extcon/extcon-arizona.c
|
||||
|
@ -511,7 +511,7 @@ config XPOWER_PMIC_OPREGION
|
||||
|
||||
config BXT_WC_PMIC_OPREGION
|
||||
bool "ACPI operation region support for BXT WhiskeyCove PMIC"
|
||||
depends on INTEL_SOC_PMIC
|
||||
depends on INTEL_SOC_PMIC_BXTWC
|
||||
help
|
||||
This config adds ACPI operation region support for BXT WhiskeyCove PMIC.
|
||||
|
||||
|
@ -1054,7 +1054,7 @@ config GPIO_UCB1400
|
||||
|
||||
config GPIO_WHISKEY_COVE
|
||||
tristate "GPIO support for Whiskey Cove PMIC"
|
||||
depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC
|
||||
depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC_BXTWC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Support for GPIO pins on Whiskey Cove PMIC.
|
||||
|
@ -263,7 +263,7 @@ static const struct gpio_chip template_chip = {
|
||||
static int wm831x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
|
||||
struct wm831x_pdata *pdata = &wm831x->pdata;
|
||||
struct wm831x_gpio *wm831x_gpio;
|
||||
int ret;
|
||||
|
||||
@ -280,6 +280,9 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
|
||||
wm831x_gpio->gpio_chip.base = pdata->gpio_base;
|
||||
else
|
||||
wm831x_gpio->gpio_chip.base = -1;
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
wm831x_gpio->gpio_chip.of_node = wm831x->dev->of_node;
|
||||
#endif
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip,
|
||||
wm831x_gpio);
|
||||
|
@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called at91-sama5d2_adc.
|
||||
|
||||
config AXP20X_ADC
|
||||
tristate "X-Powers AXP20X and AXP22X ADC driver"
|
||||
depends on MFD_AXP20X
|
||||
help
|
||||
Say yes here to have support for X-Powers power management IC (PMIC)
|
||||
AXP20X and AXP22X ADC devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called axp20x_adc.
|
||||
|
||||
config AXP288_ADC
|
||||
tristate "X-Powers AXP288 ADC driver"
|
||||
depends on MFD_AXP20X
|
||||
@ -229,6 +239,19 @@ config EXYNOS_ADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called exynos_adc.
|
||||
|
||||
config MXS_LRADC_ADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC ADC"
|
||||
depends on MFD_MXS_LRADC
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the ADC functions of the
|
||||
i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
|
||||
battery voltage measurement, and die temperature measurement.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mxs-lradc-adc.
|
||||
|
||||
config FSL_MX25_ADC
|
||||
tristate "Freescale MX25 ADC driver"
|
||||
depends on MFD_MX25_TSADC
|
||||
@ -411,20 +434,6 @@ config MESON_SARADC
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called meson_saradc.
|
||||
|
||||
config MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
|
||||
depends on INPUT
|
||||
select STMP_DEVICE
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for i.MX23/i.MX28 LRADC convertor
|
||||
built into these chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mxs-lradc.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
|
||||
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
|
||||
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
|
||||
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
|
||||
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
@ -39,7 +40,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
|
617
drivers/iio/adc/axp20x_adc.c
Normal file
617
drivers/iio/adc/axp20x_adc.c
Normal file
@ -0,0 +1,617 @@
|
||||
/* ADC driver for AXP20X and AXP22X PMICs
|
||||
*
|
||||
* Copyright (c) 2016 Free Electrons NextThing Co.
|
||||
* Quentin Schulz <quentin.schulz@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
|
||||
#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
|
||||
|
||||
#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
|
||||
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
|
||||
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
|
||||
|
||||
#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
|
||||
#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
|
||||
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
|
||||
|
||||
#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.address = _reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = _name, \
|
||||
}
|
||||
|
||||
#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.address = _reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) |\
|
||||
BIT(IIO_CHAN_INFO_OFFSET),\
|
||||
.datasheet_name = _name, \
|
||||
}
|
||||
|
||||
struct axp_data;
|
||||
|
||||
struct axp20x_adc_iio {
|
||||
struct regmap *regmap;
|
||||
struct axp_data *data;
|
||||
};
|
||||
|
||||
enum axp20x_adc_channel_v {
|
||||
AXP20X_ACIN_V = 0,
|
||||
AXP20X_VBUS_V,
|
||||
AXP20X_TS_IN,
|
||||
AXP20X_GPIO0_V,
|
||||
AXP20X_GPIO1_V,
|
||||
AXP20X_IPSOUT_V,
|
||||
AXP20X_BATT_V,
|
||||
};
|
||||
|
||||
enum axp20x_adc_channel_i {
|
||||
AXP20X_ACIN_I = 0,
|
||||
AXP20X_VBUS_I,
|
||||
AXP20X_BATT_CHRG_I,
|
||||
AXP20X_BATT_DISCHRG_I,
|
||||
};
|
||||
|
||||
enum axp22x_adc_channel_v {
|
||||
AXP22X_TS_IN = 0,
|
||||
AXP22X_BATT_V,
|
||||
};
|
||||
|
||||
enum axp22x_adc_channel_i {
|
||||
AXP22X_BATT_CHRG_I = 1,
|
||||
AXP22X_BATT_DISCHRG_I,
|
||||
};
|
||||
|
||||
static struct iio_map axp20x_maps[] = {
|
||||
{
|
||||
.consumer_dev_name = "axp20x-usb-power-supply",
|
||||
.consumer_channel = "vbus_v",
|
||||
.adc_channel_label = "vbus_v",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-usb-power-supply",
|
||||
.consumer_channel = "vbus_i",
|
||||
.adc_channel_label = "vbus_i",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-ac-power-supply",
|
||||
.consumer_channel = "acin_v",
|
||||
.adc_channel_label = "acin_v",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-ac-power-supply",
|
||||
.consumer_channel = "acin_i",
|
||||
.adc_channel_label = "acin_i",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_v",
|
||||
.adc_channel_label = "batt_v",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_chrg_i",
|
||||
.adc_channel_label = "batt_chrg_i",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_dischrg_i",
|
||||
.adc_channel_label = "batt_dischrg_i",
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct iio_map axp22x_maps[] = {
|
||||
{
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_v",
|
||||
.adc_channel_label = "batt_v",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_chrg_i",
|
||||
.adc_channel_label = "batt_chrg_i",
|
||||
}, {
|
||||
.consumer_dev_name = "axp20x-battery-power-supply",
|
||||
.consumer_channel = "batt_dischrg_i",
|
||||
.adc_channel_label = "batt_dischrg_i",
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Channels are mapped by physical system. Their channels share the same index.
|
||||
* i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
|
||||
* The only exception is for the battery. batt_v will be in_voltage6_raw and
|
||||
* charge current in_current6_raw and discharge current will be in_current7_raw.
|
||||
*/
|
||||
static const struct iio_chan_spec axp20x_adc_channels[] = {
|
||||
AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
|
||||
AXP20X_ACIN_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
|
||||
AXP20X_ACIN_I_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
|
||||
AXP20X_VBUS_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
|
||||
AXP20X_VBUS_I_ADC_H),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = AXP20X_TEMP_ADC_H,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "pmic_temp",
|
||||
},
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
|
||||
AXP20X_GPIO0_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
|
||||
AXP20X_GPIO1_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
|
||||
AXP20X_IPSOUT_V_HIGH_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
|
||||
AXP20X_BATT_V_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_CHRG_I_H),
|
||||
AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_DISCHRG_I_H),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec axp22x_adc_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = AXP22X_PMIC_TEMP_H,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "pmic_temp",
|
||||
},
|
||||
AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
|
||||
AXP20X_BATT_V_H),
|
||||
AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_CHRG_I_H),
|
||||
AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_DISCHRG_I_H),
|
||||
};
|
||||
|
||||
static int axp20x_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
int size = 12;
|
||||
|
||||
/*
|
||||
* N.B.: Unlike the Chinese datasheets tell, the charging current is
|
||||
* stored on 12 bits, not 13 bits. Only discharging current is on 13
|
||||
* bits.
|
||||
*/
|
||||
if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I)
|
||||
size = 13;
|
||||
else
|
||||
size = 12;
|
||||
|
||||
*val = axp20x_read_variable_width(info->regmap, chan->address, size);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp22x_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
int size;
|
||||
|
||||
/*
|
||||
* N.B.: Unlike the Chinese datasheets tell, the charging current is
|
||||
* stored on 12 bits, not 13 bits. Only discharging current is on 13
|
||||
* bits.
|
||||
*/
|
||||
if (chan->type == IIO_CURRENT && chan->channel == AXP22X_BATT_DISCHRG_I)
|
||||
size = 13;
|
||||
else
|
||||
size = 12;
|
||||
|
||||
*val = axp20x_read_variable_width(info->regmap, chan->address, size);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
case AXP20X_ACIN_V:
|
||||
case AXP20X_VBUS_V:
|
||||
*val = 1;
|
||||
*val2 = 700000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP20X_GPIO0_V:
|
||||
case AXP20X_GPIO1_V:
|
||||
*val = 0;
|
||||
*val2 = 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP20X_BATT_V:
|
||||
*val = 1;
|
||||
*val2 = 100000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP20X_IPSOUT_V:
|
||||
*val = 1;
|
||||
*val2 = 400000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale_current(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
case AXP20X_ACIN_I:
|
||||
*val = 0;
|
||||
*val2 = 625000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP20X_VBUS_I:
|
||||
*val = 0;
|
||||
*val2 = 375000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP20X_BATT_DISCHRG_I:
|
||||
case AXP20X_BATT_CHRG_I:
|
||||
*val = 0;
|
||||
*val2 = 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
int *val2)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return axp20x_adc_scale_voltage(chan->channel, val, val2);
|
||||
|
||||
case IIO_CURRENT:
|
||||
return axp20x_adc_scale_current(chan->channel, val, val2);
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = 100;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
int *val2)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->channel != AXP22X_BATT_V)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 1;
|
||||
*val2 = 100000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case IIO_CURRENT:
|
||||
*val = 0;
|
||||
*val2 = 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = 100;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
||||
int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (channel) {
|
||||
case AXP20X_GPIO0_V:
|
||||
*val &= AXP20X_GPIO10_IN_RANGE_GPIO0;
|
||||
break;
|
||||
|
||||
case AXP20X_GPIO1_V:
|
||||
*val &= AXP20X_GPIO10_IN_RANGE_GPIO1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = !!(*val) * 700000;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp20x_adc_offset(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return axp20x_adc_offset_voltage(indio_dev, chan->channel, val);
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = -1447;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
return axp20x_adc_offset(indio_dev, chan, val);
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return axp20x_adc_scale(chan, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return axp20x_adc_raw(indio_dev, chan, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp22x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -2677;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return axp22x_adc_scale(chan, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return axp22x_adc_raw(indio_dev, chan, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
unsigned int reg, regval;
|
||||
|
||||
/*
|
||||
* The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
|
||||
* for (independently) GPIO0 and GPIO1 when in ADC mode.
|
||||
*/
|
||||
if (mask != IIO_CHAN_INFO_OFFSET)
|
||||
return -EINVAL;
|
||||
|
||||
if (val != 0 && val != 700000)
|
||||
return -EINVAL;
|
||||
|
||||
switch (chan->channel) {
|
||||
case AXP20X_GPIO0_V:
|
||||
reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val);
|
||||
break;
|
||||
|
||||
case AXP20X_GPIO1_V:
|
||||
reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg,
|
||||
regval);
|
||||
}
|
||||
|
||||
static const struct iio_info axp20x_adc_iio_info = {
|
||||
.read_raw = axp20x_read_raw,
|
||||
.write_raw = axp20x_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info axp22x_adc_iio_info = {
|
||||
.read_raw = axp22x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int axp20x_adc_rate(int rate)
|
||||
{
|
||||
return AXP20X_ADC_RATE_HZ(rate);
|
||||
}
|
||||
|
||||
static int axp22x_adc_rate(int rate)
|
||||
{
|
||||
return AXP22X_ADC_RATE_HZ(rate);
|
||||
}
|
||||
|
||||
struct axp_data {
|
||||
const struct iio_info *iio_info;
|
||||
int num_channels;
|
||||
struct iio_chan_spec const *channels;
|
||||
unsigned long adc_en1_mask;
|
||||
int (*adc_rate)(int rate);
|
||||
bool adc_en2;
|
||||
struct iio_map *maps;
|
||||
};
|
||||
|
||||
static const struct axp_data axp20x_data = {
|
||||
.iio_info = &axp20x_adc_iio_info,
|
||||
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
|
||||
.channels = axp20x_adc_channels,
|
||||
.adc_en1_mask = AXP20X_ADC_EN1_MASK,
|
||||
.adc_rate = axp20x_adc_rate,
|
||||
.adc_en2 = true,
|
||||
.maps = axp20x_maps,
|
||||
};
|
||||
|
||||
static const struct axp_data axp22x_data = {
|
||||
.iio_info = &axp22x_adc_iio_info,
|
||||
.num_channels = ARRAY_SIZE(axp22x_adc_channels),
|
||||
.channels = axp22x_adc_channels,
|
||||
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
|
||||
.adc_rate = axp22x_adc_rate,
|
||||
.adc_en2 = false,
|
||||
.maps = axp22x_maps,
|
||||
};
|
||||
|
||||
static const struct platform_device_id axp20x_adc_id_match[] = {
|
||||
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
|
||||
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
|
||||
|
||||
static int axp20x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_adc_iio *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct axp20x_dev *axp20x_dev;
|
||||
int ret;
|
||||
|
||||
axp20x_dev = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
info->regmap = axp20x_dev->regmap;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data;
|
||||
|
||||
indio_dev->name = platform_get_device_id(pdev)->name;
|
||||
indio_dev->info = info->data->iio_info;
|
||||
indio_dev->num_channels = info->data->num_channels;
|
||||
indio_dev->channels = info->data->channels;
|
||||
|
||||
/* Enable the ADCs on IP */
|
||||
regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
|
||||
|
||||
if (info->data->adc_en2)
|
||||
/* Enable GPIO0/1 and internal temperature ADCs */
|
||||
regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
|
||||
AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
|
||||
|
||||
/* Configure ADCs rate */
|
||||
regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
|
||||
info->data->adc_rate(100));
|
||||
|
||||
ret = iio_map_array_register(indio_dev, info->data->maps);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret);
|
||||
goto fail_map;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not register the device\n");
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
fail_map:
|
||||
regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
|
||||
|
||||
if (info->data->adc_en2)
|
||||
regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int axp20x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
|
||||
|
||||
if (info->data->adc_en2)
|
||||
regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver axp20x_adc_driver = {
|
||||
.driver = {
|
||||
.name = "axp20x-adc",
|
||||
},
|
||||
.id_table = axp20x_adc_id_match,
|
||||
.probe = axp20x_probe,
|
||||
.remove = axp20x_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(axp20x_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
|
||||
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL");
|
843
drivers/iio/adc/mxs-lradc-adc.c
Normal file
843
drivers/iio/adc/mxs-lradc-adc.c
Normal file
@ -0,0 +1,843 @@
|
||||
/*
|
||||
* Freescale MXS LRADC ADC driver
|
||||
*
|
||||
* Copyright (c) 2012 DENX Software Engineering, GmbH.
|
||||
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* Authors:
|
||||
* Marek Vasut <marex@denx.de>
|
||||
* Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mxs-lradc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/*
|
||||
* Make this runtime configurable if necessary. Currently, if the buffered mode
|
||||
* is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
|
||||
* triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
|
||||
* seconds. The result is that the samples arrive every 500mS.
|
||||
*/
|
||||
#define LRADC_DELAY_TIMER_PER 200
|
||||
#define LRADC_DELAY_TIMER_LOOP 5
|
||||
|
||||
#define VREF_MV_BASE 1850
|
||||
|
||||
const char *mx23_lradc_adc_irq_names[] = {
|
||||
"mxs-lradc-channel0",
|
||||
"mxs-lradc-channel1",
|
||||
"mxs-lradc-channel2",
|
||||
"mxs-lradc-channel3",
|
||||
"mxs-lradc-channel4",
|
||||
"mxs-lradc-channel5",
|
||||
};
|
||||
|
||||
const char *mx28_lradc_adc_irq_names[] = {
|
||||
"mxs-lradc-thresh0",
|
||||
"mxs-lradc-thresh1",
|
||||
"mxs-lradc-channel0",
|
||||
"mxs-lradc-channel1",
|
||||
"mxs-lradc-channel2",
|
||||
"mxs-lradc-channel3",
|
||||
"mxs-lradc-channel4",
|
||||
"mxs-lradc-channel5",
|
||||
"mxs-lradc-button0",
|
||||
"mxs-lradc-button1",
|
||||
};
|
||||
|
||||
static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
|
||||
[IMX23_LRADC] = {
|
||||
VREF_MV_BASE, /* CH0 */
|
||||
VREF_MV_BASE, /* CH1 */
|
||||
VREF_MV_BASE, /* CH2 */
|
||||
VREF_MV_BASE, /* CH3 */
|
||||
VREF_MV_BASE, /* CH4 */
|
||||
VREF_MV_BASE, /* CH5 */
|
||||
VREF_MV_BASE * 2, /* CH6 VDDIO */
|
||||
VREF_MV_BASE * 4, /* CH7 VBATT */
|
||||
VREF_MV_BASE, /* CH8 Temp sense 0 */
|
||||
VREF_MV_BASE, /* CH9 Temp sense 1 */
|
||||
VREF_MV_BASE, /* CH10 */
|
||||
VREF_MV_BASE, /* CH11 */
|
||||
VREF_MV_BASE, /* CH12 USB_DP */
|
||||
VREF_MV_BASE, /* CH13 USB_DN */
|
||||
VREF_MV_BASE, /* CH14 VBG */
|
||||
VREF_MV_BASE * 4, /* CH15 VDD5V */
|
||||
},
|
||||
[IMX28_LRADC] = {
|
||||
VREF_MV_BASE, /* CH0 */
|
||||
VREF_MV_BASE, /* CH1 */
|
||||
VREF_MV_BASE, /* CH2 */
|
||||
VREF_MV_BASE, /* CH3 */
|
||||
VREF_MV_BASE, /* CH4 */
|
||||
VREF_MV_BASE, /* CH5 */
|
||||
VREF_MV_BASE, /* CH6 */
|
||||
VREF_MV_BASE * 4, /* CH7 VBATT */
|
||||
VREF_MV_BASE, /* CH8 Temp sense 0 */
|
||||
VREF_MV_BASE, /* CH9 Temp sense 1 */
|
||||
VREF_MV_BASE * 2, /* CH10 VDDIO */
|
||||
VREF_MV_BASE, /* CH11 VTH */
|
||||
VREF_MV_BASE * 2, /* CH12 VDDA */
|
||||
VREF_MV_BASE, /* CH13 VDDD */
|
||||
VREF_MV_BASE, /* CH14 VBG */
|
||||
VREF_MV_BASE * 4, /* CH15 VDD5V */
|
||||
},
|
||||
};
|
||||
|
||||
enum mxs_lradc_divbytwo {
|
||||
MXS_LRADC_DIV_DISABLED = 0,
|
||||
MXS_LRADC_DIV_ENABLED,
|
||||
};
|
||||
|
||||
struct mxs_lradc_scale {
|
||||
unsigned int integer;
|
||||
unsigned int nano;
|
||||
};
|
||||
|
||||
struct mxs_lradc_adc {
|
||||
struct mxs_lradc *lradc;
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
u32 buffer[10];
|
||||
struct iio_trigger *trig;
|
||||
struct completion completion;
|
||||
spinlock_t lock;
|
||||
|
||||
const u32 *vref_mv;
|
||||
struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
|
||||
unsigned long is_divided;
|
||||
};
|
||||
|
||||
|
||||
/* Raw I/O operations */
|
||||
static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
|
||||
int *val)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
|
||||
struct mxs_lradc *lradc = adc->lradc;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* See if there is no buffered operation in progress. If there is simply
|
||||
* bail out. This can be improved to support both buffered and raw IO at
|
||||
* the same time, yet the code becomes horribly complicated. Therefore I
|
||||
* applied KISS principle here.
|
||||
*/
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reinit_completion(&adc->completion);
|
||||
|
||||
/*
|
||||
* No buffered operation in progress, map the channel and trigger it.
|
||||
* Virtual channel 0 is always used here as the others are always not
|
||||
* used if doing raw sampling.
|
||||
*/
|
||||
if (lradc->soc == IMX28_LRADC)
|
||||
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* Enable / disable the divider per requirement */
|
||||
if (test_bit(chan, &adc->is_divided))
|
||||
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
|
||||
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
|
||||
else
|
||||
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
|
||||
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* Clean the slot's previous content, then set new one. */
|
||||
writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
|
||||
adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(0, adc->base + LRADC_CH(0));
|
||||
|
||||
/* Enable the IRQ and start sampling the channel. */
|
||||
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* Wait for completion on the channel, 1 second max. */
|
||||
ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Read the data. */
|
||||
*val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
err:
|
||||
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
|
||||
{
|
||||
int ret, min, max;
|
||||
|
||||
ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
|
||||
if (ret != IIO_VAL_INT)
|
||||
return ret;
|
||||
|
||||
ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
|
||||
if (ret != IIO_VAL_INT)
|
||||
return ret;
|
||||
|
||||
*val = max - min;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_TEMP)
|
||||
return mxs_lradc_adc_read_temp(iio_dev, val);
|
||||
|
||||
return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* From the datasheet, we have to multiply by 1.012 and
|
||||
* divide by 4
|
||||
*/
|
||||
*val = 0;
|
||||
*val2 = 253000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
*val = adc->vref_mv[chan->channel];
|
||||
*val2 = chan->scan_type.realbits -
|
||||
test_bit(chan->channel, &adc->is_divided);
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* The calculated value from the ADC is in Kelvin, we
|
||||
* want Celsius for hwmon so the offset is -273.15
|
||||
* The offset is applied before scaling so it is
|
||||
* actually -213.15 * 4 / 1.012 = -1079.644268
|
||||
*/
|
||||
*val = -1079;
|
||||
*val2 = 644268;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int val, int val2, long m)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
|
||||
struct mxs_lradc_scale *scale_avail =
|
||||
adc->scale_avail[chan->channel];
|
||||
int ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
|
||||
val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
|
||||
/* divider by two disabled */
|
||||
clear_bit(chan->channel, &adc->is_divided);
|
||||
ret = 0;
|
||||
} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
|
||||
val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
|
||||
/* divider by two enabled */
|
||||
set_bit(chan->channel, &adc->is_divided);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
long m)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio = dev_to_iio_dev(dev);
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
|
||||
int i, ch, len = 0;
|
||||
|
||||
ch = iio_attr->address;
|
||||
for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
|
||||
len += sprintf(buf + len, "%u.%09u ",
|
||||
adc->scale_avail[ch][i].integer,
|
||||
adc->scale_avail[ch][i].nano);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#define SHOW_SCALE_AVAILABLE_ATTR(ch)\
|
||||
IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\
|
||||
mxs_lradc_adc_show_scale_avail, NULL, ch)
|
||||
|
||||
SHOW_SCALE_AVAILABLE_ATTR(0);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(1);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(2);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(3);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(4);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(5);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(6);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(7);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(10);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(11);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(12);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(13);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(14);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(15);
|
||||
|
||||
static struct attribute *mxs_lradc_adc_attributes[] = {
|
||||
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group mxs_lradc_adc_attribute_group = {
|
||||
.attrs = mxs_lradc_adc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info mxs_lradc_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mxs_lradc_adc_read_raw,
|
||||
.write_raw = mxs_lradc_adc_write_raw,
|
||||
.write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt,
|
||||
.attrs = &mxs_lradc_adc_attribute_group,
|
||||
};
|
||||
|
||||
/* IRQ Handling */
|
||||
static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *iio = data;
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
struct mxs_lradc *lradc = adc->lradc;
|
||||
unsigned long reg = readl(adc->base + LRADC_CTRL1);
|
||||
unsigned long flags;
|
||||
|
||||
if (!(reg & mxs_lradc_irq_mask(lradc)))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (iio_buffer_enabled(iio)) {
|
||||
if (reg & lradc->buffer_vchans) {
|
||||
spin_lock_irqsave(&adc->lock, flags);
|
||||
iio_trigger_poll(iio->trig);
|
||||
spin_unlock_irqrestore(&adc->lock, flags);
|
||||
}
|
||||
} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
|
||||
complete(&adc->completion);
|
||||
}
|
||||
|
||||
writel(reg & mxs_lradc_irq_mask(lradc),
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* Trigger handling */
|
||||
static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio = pf->indio_dev;
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
const u32 chan_value = LRADC_CH_ACCUMULATE |
|
||||
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
|
||||
unsigned int i, j = 0;
|
||||
|
||||
for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
||||
adc->buffer[j] = readl(adc->base + LRADC_CH(j));
|
||||
writel(chan_value, adc->base + LRADC_CH(j));
|
||||
adc->buffer[j] &= LRADC_CH_VALUE_MASK;
|
||||
adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
|
||||
j++;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(iio->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *iio = iio_trigger_get_drvdata(trig);
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
|
||||
|
||||
writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &mxs_lradc_adc_configure_trigger,
|
||||
};
|
||||
|
||||
static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
|
||||
{
|
||||
int ret;
|
||||
struct iio_trigger *trig;
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
|
||||
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
|
||||
iio->id);
|
||||
|
||||
trig->dev.parent = adc->dev;
|
||||
iio_trigger_set_drvdata(trig, iio);
|
||||
trig->ops = &mxs_lradc_adc_trigger_ops;
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adc->trig = trig;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
|
||||
iio_trigger_unregister(adc->trig);
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
struct mxs_lradc *lradc = adc->lradc;
|
||||
int chan, ofs = 0;
|
||||
unsigned long enable = 0;
|
||||
u32 ctrl4_set = 0;
|
||||
u32 ctrl4_clr = 0;
|
||||
u32 ctrl1_irq = 0;
|
||||
const u32 chan_value = LRADC_CH_ACCUMULATE |
|
||||
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
|
||||
|
||||
if (lradc->soc == IMX28_LRADC)
|
||||
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(lradc->buffer_vchans,
|
||||
adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
||||
ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
|
||||
ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
|
||||
ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
|
||||
writel(chan_value, adc->base + LRADC_CH(ofs));
|
||||
bitmap_set(&enable, ofs, 1);
|
||||
ofs++;
|
||||
}
|
||||
|
||||
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
|
||||
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
|
||||
writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
|
||||
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
struct mxs_lradc *lradc = adc->lradc;
|
||||
|
||||
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
|
||||
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
|
||||
|
||||
writel(lradc->buffer_vchans,
|
||||
adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
if (lradc->soc == IMX28_LRADC)
|
||||
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
|
||||
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
|
||||
const unsigned long *mask)
|
||||
{
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
struct mxs_lradc *lradc = adc->lradc;
|
||||
const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
|
||||
int rsvd_chans = 0;
|
||||
unsigned long rsvd_mask = 0;
|
||||
|
||||
if (lradc->use_touchbutton)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
|
||||
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
|
||||
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
|
||||
|
||||
if (lradc->use_touchbutton)
|
||||
rsvd_chans++;
|
||||
if (lradc->touchscreen_wire)
|
||||
rsvd_chans += 2;
|
||||
|
||||
/* Test for attempts to map channels with special mode of operation. */
|
||||
if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
|
||||
return false;
|
||||
|
||||
/* Test for attempts to map more channels then available slots. */
|
||||
if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
|
||||
.preenable = &mxs_lradc_adc_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &mxs_lradc_adc_buffer_postdisable,
|
||||
.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
|
||||
};
|
||||
|
||||
/* Driver initialization */
|
||||
#define MXS_ADC_CHAN(idx, chan_type, name) { \
|
||||
.type = (chan_type), \
|
||||
.indexed = 1, \
|
||||
.scan_index = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.channel = (idx), \
|
||||
.address = (idx), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = LRADC_RESOLUTION, \
|
||||
.storagebits = 32, \
|
||||
}, \
|
||||
.datasheet_name = (name), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
|
||||
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
|
||||
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
|
||||
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
|
||||
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
|
||||
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
|
||||
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
|
||||
MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
|
||||
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
|
||||
/* Combined Temperature sensors */
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.scan_index = 8,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.channel = 8,
|
||||
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
|
||||
.datasheet_name = "TEMP_DIE",
|
||||
},
|
||||
/* Hidden channel to keep indexes */
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.scan_index = -1,
|
||||
.channel = 9,
|
||||
},
|
||||
MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
|
||||
MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
|
||||
MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
|
||||
MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
|
||||
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
|
||||
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
|
||||
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
|
||||
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
|
||||
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
|
||||
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
|
||||
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
|
||||
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
|
||||
MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
|
||||
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
|
||||
/* Combined Temperature sensors */
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.scan_index = 8,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.channel = 8,
|
||||
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
|
||||
.datasheet_name = "TEMP_DIE",
|
||||
},
|
||||
/* Hidden channel to keep indexes */
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.scan_index = -1,
|
||||
.channel = 9,
|
||||
},
|
||||
MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
|
||||
MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
|
||||
MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
|
||||
MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
|
||||
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
|
||||
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
|
||||
};
|
||||
|
||||
static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
|
||||
{
|
||||
/* The ADC always uses DELAY CHANNEL 0. */
|
||||
const u32 adc_cfg =
|
||||
(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
|
||||
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
|
||||
|
||||
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
|
||||
writel(adc_cfg, adc->base + LRADC_DELAY(0));
|
||||
|
||||
/*
|
||||
* Start internal temperature sensing by clearing bit
|
||||
* HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared
|
||||
* after power up.
|
||||
*/
|
||||
writel(0, adc->base + LRADC_CTRL2);
|
||||
}
|
||||
|
||||
static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
|
||||
{
|
||||
writel(0, adc->base + LRADC_DELAY(0));
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
|
||||
struct mxs_lradc_adc *adc;
|
||||
struct iio_dev *iio;
|
||||
struct resource *iores;
|
||||
int ret, irq, virq, i, s, n;
|
||||
u64 scale_uv;
|
||||
const char **irq_name;
|
||||
|
||||
/* Allocate the IIO device. */
|
||||
iio = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!iio) {
|
||||
dev_err(dev, "Failed to allocate IIO device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
adc = iio_priv(iio);
|
||||
adc->lradc = lradc;
|
||||
adc->dev = dev;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap(dev, iores->start, resource_size(iores));
|
||||
if (IS_ERR(adc->base))
|
||||
return PTR_ERR(adc->base);
|
||||
|
||||
init_completion(&adc->completion);
|
||||
spin_lock_init(&adc->lock);
|
||||
|
||||
platform_set_drvdata(pdev, iio);
|
||||
|
||||
iio->name = pdev->name;
|
||||
iio->dev.parent = dev;
|
||||
iio->dev.of_node = dev->parent->of_node;
|
||||
iio->info = &mxs_lradc_adc_iio_info;
|
||||
iio->modes = INDIO_DIRECT_MODE;
|
||||
iio->masklength = LRADC_MAX_TOTAL_CHANS;
|
||||
|
||||
if (lradc->soc == IMX23_LRADC) {
|
||||
iio->channels = mx23_lradc_chan_spec;
|
||||
iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
|
||||
irq_name = mx23_lradc_adc_irq_names;
|
||||
n = ARRAY_SIZE(mx23_lradc_adc_irq_names);
|
||||
} else {
|
||||
iio->channels = mx28_lradc_chan_spec;
|
||||
iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
|
||||
irq_name = mx28_lradc_adc_irq_names;
|
||||
n = ARRAY_SIZE(mx28_lradc_adc_irq_names);
|
||||
}
|
||||
|
||||
ret = stmp_reset_block(adc->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
irq = platform_get_irq_byname(pdev, irq_name[i]);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
virq = irq_of_parse_and_map(dev->parent->of_node, irq);
|
||||
|
||||
ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
|
||||
0, irq_name[i], iio);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mxs_lradc_adc_trigger_init(iio);
|
||||
if (ret)
|
||||
goto err_trig;
|
||||
|
||||
ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
|
||||
&mxs_lradc_adc_trigger_handler,
|
||||
&mxs_lradc_adc_buffer_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
|
||||
|
||||
/* Populate available ADC input ranges */
|
||||
for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
|
||||
for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
|
||||
/*
|
||||
* [s=0] = optional divider by two disabled (default)
|
||||
* [s=1] = optional divider by two enabled
|
||||
*
|
||||
* The scale is calculated by doing:
|
||||
* Vref >> (realbits - s)
|
||||
* which multiplies by two on the second component
|
||||
* of the array.
|
||||
*/
|
||||
scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
|
||||
(LRADC_RESOLUTION - s);
|
||||
adc->scale_avail[i][s].nano =
|
||||
do_div(scale_uv, 100000000) * 10;
|
||||
adc->scale_avail[i][s].integer = scale_uv;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure the hardware. */
|
||||
mxs_lradc_adc_hw_init(adc);
|
||||
|
||||
/* Register IIO device. */
|
||||
ret = iio_device_register(iio);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IIO device\n");
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_dev:
|
||||
mxs_lradc_adc_hw_stop(adc);
|
||||
mxs_lradc_adc_trigger_remove(iio);
|
||||
err_trig:
|
||||
iio_triggered_buffer_cleanup(iio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_lradc_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *iio = platform_get_drvdata(pdev);
|
||||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
|
||||
iio_device_unregister(iio);
|
||||
mxs_lradc_adc_hw_stop(adc);
|
||||
mxs_lradc_adc_trigger_remove(iio);
|
||||
iio_triggered_buffer_cleanup(iio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mxs_lradc_adc_driver = {
|
||||
.driver = {
|
||||
.name = "mxs-lradc-adc",
|
||||
},
|
||||
.probe = mxs_lradc_adc_probe,
|
||||
.remove = mxs_lradc_adc_remove,
|
||||
};
|
||||
module_platform_driver(mxs_lradc_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mxs-lradc-adc");
|
File diff suppressed because it is too large
Load Diff
@ -316,6 +316,16 @@ config INPUT_COBALT_BTNS
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cobalt_btns.
|
||||
|
||||
config INPUT_CPCAP_PWRBUTTON
|
||||
tristate "CPCAP OnKey"
|
||||
depends on MFD_CPCAP
|
||||
help
|
||||
Say Y here if you want to enable power key reporting via the
|
||||
Motorola CPCAP chip.
|
||||
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called cpcap-pwrbutton.
|
||||
|
||||
config INPUT_WISTRON_BTNS
|
||||
tristate "x86 Wistron laptop button interface"
|
||||
depends on X86_32
|
||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o
|
||||
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
|
||||
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
|
||||
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
|
||||
obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
|
||||
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
|
||||
obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o
|
||||
|
117
drivers/input/misc/cpcap-pwrbutton.c
Normal file
117
drivers/input/misc/cpcap-pwrbutton.c
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* CPCAP Power Button Input Driver
|
||||
*
|
||||
* Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/motorola-cpcap.h>
|
||||
|
||||
#define CPCAP_IRQ_ON 23
|
||||
#define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16))
|
||||
|
||||
struct cpcap_power_button {
|
||||
struct regmap *regmap;
|
||||
struct input_dev *idev;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static irqreturn_t powerbutton_irq(int irq, void *_button)
|
||||
{
|
||||
struct cpcap_power_button *button = _button;
|
||||
int val;
|
||||
|
||||
val = cpcap_sense_virq(button->regmap, irq);
|
||||
if (val < 0) {
|
||||
dev_err(button->dev, "irq read failed: %d", val);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
pm_wakeup_event(button->dev, 0);
|
||||
input_report_key(button->idev, KEY_POWER, val);
|
||||
input_sync(button->idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cpcap_power_button_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cpcap_power_button *button;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int err;
|
||||
|
||||
button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
||||
button->idev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!button->idev)
|
||||
return -ENOMEM;
|
||||
|
||||
button->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!button->regmap)
|
||||
return -ENODEV;
|
||||
|
||||
button->dev = &pdev->dev;
|
||||
|
||||
button->idev->name = "cpcap-pwrbutton";
|
||||
button->idev->phys = "cpcap-pwrbutton/input0";
|
||||
button->idev->dev.parent = button->dev;
|
||||
input_set_capability(button->idev, EV_KEY, KEY_POWER);
|
||||
|
||||
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "IRQ request failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = input_register_device(button->idev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Input register failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = {
|
||||
{ .compatible = "motorola,cpcap-pwrbutton" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver cpcap_power_button_driver = {
|
||||
.probe = cpcap_power_button_probe,
|
||||
.driver = {
|
||||
.name = "cpcap-pwrbutton",
|
||||
.of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(cpcap_power_button_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cpcap-pwrbutton");
|
||||
MODULE_DESCRIPTION("CPCAP Power Button");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
|
@ -829,6 +829,16 @@ config TOUCHSCREEN_USB_COMPOSITE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbtouchscreen.
|
||||
|
||||
config TOUCHSCREEN_MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
|
||||
depends on MFD_MXS_LRADC
|
||||
help
|
||||
Say Y here if you have a touchscreen connected to the low-resolution
|
||||
analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called mxs-lradc-ts.
|
||||
|
||||
config TOUCHSCREEN_MX25
|
||||
tristate "Freescale i.MX25 touchscreen input driver"
|
||||
depends on MFD_MX25_TSADC
|
||||
|
@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC) += mxs-lradc-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
|
||||
|
714
drivers/input/touchscreen/mxs-lradc-ts.c
Normal file
714
drivers/input/touchscreen/mxs-lradc-ts.c
Normal file
@ -0,0 +1,714 @@
|
||||
/*
|
||||
* Freescale MXS LRADC touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2012 DENX Software Engineering, GmbH.
|
||||
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* Authors:
|
||||
* Marek Vasut <marex@denx.de>
|
||||
* Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mxs-lradc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
const char *mxs_lradc_ts_irq_names[] = {
|
||||
"mxs-lradc-touchscreen",
|
||||
"mxs-lradc-channel6",
|
||||
"mxs-lradc-channel7",
|
||||
};
|
||||
|
||||
/*
|
||||
* Touchscreen handling
|
||||
*/
|
||||
enum mxs_lradc_ts_plate {
|
||||
LRADC_TOUCH = 0,
|
||||
LRADC_SAMPLE_X,
|
||||
LRADC_SAMPLE_Y,
|
||||
LRADC_SAMPLE_PRESSURE,
|
||||
LRADC_SAMPLE_VALID,
|
||||
};
|
||||
|
||||
struct mxs_lradc_ts {
|
||||
struct mxs_lradc *lradc;
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
/*
|
||||
* When the touchscreen is enabled, we give it two private virtual
|
||||
* channels: #6 and #7. This means that only 6 virtual channels (instead
|
||||
* of 8) will be available for buffered capture.
|
||||
*/
|
||||
#define TOUCHSCREEN_VCHANNEL1 7
|
||||
#define TOUCHSCREEN_VCHANNEL2 6
|
||||
|
||||
struct input_dev *ts_input;
|
||||
|
||||
enum mxs_lradc_ts_plate cur_plate; /* state machine */
|
||||
bool ts_valid;
|
||||
unsigned int ts_x_pos;
|
||||
unsigned int ts_y_pos;
|
||||
unsigned int ts_pressure;
|
||||
|
||||
/* handle touchscreen's physical behaviour */
|
||||
/* samples per coordinate */
|
||||
unsigned int over_sample_cnt;
|
||||
/* time clocks between samples */
|
||||
unsigned int over_sample_delay;
|
||||
/* time in clocks to wait after the plates where switched */
|
||||
unsigned int settling_delay;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct state_info {
|
||||
u32 mask;
|
||||
u32 bit;
|
||||
u32 x_plate;
|
||||
u32 y_plate;
|
||||
u32 pressure;
|
||||
};
|
||||
|
||||
static struct state_info info[] = {
|
||||
{LRADC_CTRL0_MX23_PLATE_MASK, LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE,
|
||||
LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM,
|
||||
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM,
|
||||
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM},
|
||||
{LRADC_CTRL0_MX28_PLATE_MASK, LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE,
|
||||
LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW,
|
||||
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW,
|
||||
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW}
|
||||
};
|
||||
|
||||
static bool mxs_lradc_check_touch_event(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
return !!(readl(ts->base + LRADC_STATUS) &
|
||||
LRADC_STATUS_TOUCH_DETECT_RAW);
|
||||
}
|
||||
|
||||
static void mxs_lradc_map_ts_channel(struct mxs_lradc_ts *ts, unsigned int vch,
|
||||
unsigned int ch)
|
||||
{
|
||||
writel(LRADC_CTRL4_LRADCSELECT_MASK(vch),
|
||||
ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL4_LRADCSELECT(vch, ch),
|
||||
ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned int ch)
|
||||
{
|
||||
/*
|
||||
* prepare for oversampling conversion
|
||||
*
|
||||
* from the datasheet:
|
||||
* "The ACCUMULATE bit in the appropriate channel register
|
||||
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
|
||||
* otherwise, the IRQs will not fire."
|
||||
*/
|
||||
writel(LRADC_CH_ACCUMULATE |
|
||||
LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
|
||||
ts->base + LRADC_CH(ch));
|
||||
|
||||
/* from the datasheet:
|
||||
* "Software must clear this register in preparation for a
|
||||
* multi-cycle accumulation.
|
||||
*/
|
||||
writel(LRADC_CH_VALUE_MASK,
|
||||
ts->base + LRADC_CH(ch) + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/*
|
||||
* prepare the delay/loop unit according to the oversampling count
|
||||
*
|
||||
* from the datasheet:
|
||||
* "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
|
||||
* HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
|
||||
* the LRADC will not trigger the delay group."
|
||||
*/
|
||||
writel(LRADC_DELAY_TRIGGER(1 << ch) | LRADC_DELAY_TRIGGER_DELAYS(0) |
|
||||
LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
|
||||
LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
|
||||
ts->base + LRADC_DELAY(3));
|
||||
|
||||
writel(LRADC_CTRL1_LRADC_IRQ(ch),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/*
|
||||
* after changing the touchscreen plates setting
|
||||
* the signals need some initial time to settle. Start the
|
||||
* SoC's delay unit and start the conversion later
|
||||
* and automatically.
|
||||
*/
|
||||
writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
|
||||
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
|
||||
ts->base + LRADC_DELAY(2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Pressure detection is special:
|
||||
* We want to do both required measurements for the pressure detection in
|
||||
* one turn. Use the hardware features to chain both conversions and let the
|
||||
* hardware report one interrupt if both conversions are done
|
||||
*/
|
||||
static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts,
|
||||
unsigned int ch1, unsigned int ch2)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* prepare for oversampling conversion
|
||||
*
|
||||
* from the datasheet:
|
||||
* "The ACCUMULATE bit in the appropriate channel register
|
||||
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
|
||||
* otherwise, the IRQs will not fire."
|
||||
*/
|
||||
reg = LRADC_CH_ACCUMULATE |
|
||||
LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
|
||||
writel(reg, ts->base + LRADC_CH(ch1));
|
||||
writel(reg, ts->base + LRADC_CH(ch2));
|
||||
|
||||
/* from the datasheet:
|
||||
* "Software must clear this register in preparation for a
|
||||
* multi-cycle accumulation.
|
||||
*/
|
||||
writel(LRADC_CH_VALUE_MASK,
|
||||
ts->base + LRADC_CH(ch1) + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CH_VALUE_MASK,
|
||||
ts->base + LRADC_CH(ch2) + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* prepare the delay/loop unit according to the oversampling count */
|
||||
writel(LRADC_DELAY_TRIGGER(1 << ch1) | LRADC_DELAY_TRIGGER(1 << ch2) |
|
||||
LRADC_DELAY_TRIGGER_DELAYS(0) |
|
||||
LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
|
||||
LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
|
||||
ts->base + LRADC_DELAY(3));
|
||||
|
||||
writel(LRADC_CTRL1_LRADC_IRQ(ch2),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/*
|
||||
* after changing the touchscreen plates setting
|
||||
* the signals need some initial time to settle. Start the
|
||||
* SoC's delay unit and start the conversion later
|
||||
* and automatically.
|
||||
*/
|
||||
writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
|
||||
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
|
||||
ts->base + LRADC_DELAY(2));
|
||||
}
|
||||
|
||||
static unsigned int mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
|
||||
unsigned int channel)
|
||||
{
|
||||
u32 reg;
|
||||
unsigned int num_samples, val;
|
||||
|
||||
reg = readl(ts->base + LRADC_CH(channel));
|
||||
if (reg & LRADC_CH_ACCUMULATE)
|
||||
num_samples = ts->over_sample_cnt;
|
||||
else
|
||||
num_samples = 1;
|
||||
|
||||
val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
|
||||
return val / num_samples;
|
||||
}
|
||||
|
||||
static unsigned int mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
|
||||
unsigned int ch1, unsigned int ch2)
|
||||
{
|
||||
u32 reg, mask;
|
||||
unsigned int pressure, m1, m2;
|
||||
|
||||
mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
|
||||
reg = readl(ts->base + LRADC_CTRL1) & mask;
|
||||
|
||||
while (reg != mask) {
|
||||
reg = readl(ts->base + LRADC_CTRL1) & mask;
|
||||
dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
|
||||
}
|
||||
|
||||
m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
|
||||
m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
|
||||
|
||||
if (m2 == 0) {
|
||||
dev_warn(ts->dev, "Cannot calculate pressure\n");
|
||||
return 1 << (LRADC_RESOLUTION - 1);
|
||||
}
|
||||
|
||||
/* simply scale the value from 0 ... max ADC resolution */
|
||||
pressure = m1;
|
||||
pressure *= (1 << LRADC_RESOLUTION);
|
||||
pressure /= m2;
|
||||
|
||||
dev_dbg(ts->dev, "Pressure = %u\n", pressure);
|
||||
return pressure;
|
||||
}
|
||||
|
||||
#define TS_CH_XP 2
|
||||
#define TS_CH_YP 3
|
||||
#define TS_CH_XM 4
|
||||
#define TS_CH_YM 5
|
||||
|
||||
/*
|
||||
* YP(open)--+-------------+
|
||||
* | |--+
|
||||
* | | |
|
||||
* YM(-)--+-------------+ |
|
||||
* +--------------+
|
||||
* | |
|
||||
* XP(weak+) XM(open)
|
||||
*
|
||||
* "weak+" means 200k Ohm VDDIO
|
||||
* (-) means GND
|
||||
*/
|
||||
static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
/*
|
||||
* In order to detect a touch event the 'touch detect enable' bit
|
||||
* enables:
|
||||
* - a weak pullup to the X+ connector
|
||||
* - a strong ground at the Y- connector
|
||||
*/
|
||||
writel(info[lradc->soc].mask,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(info[lradc->soc].bit,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
/*
|
||||
* YP(meas)--+-------------+
|
||||
* | |--+
|
||||
* | | |
|
||||
* YM(open)--+-------------+ |
|
||||
* +--------------+
|
||||
* | |
|
||||
* XP(+) XM(-)
|
||||
*
|
||||
* (+) means here 1.85 V
|
||||
* (-) means here GND
|
||||
*/
|
||||
static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
writel(info[lradc->soc].mask,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(info[lradc->soc].x_plate,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
ts->cur_plate = LRADC_SAMPLE_X;
|
||||
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
|
||||
mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
|
||||
}
|
||||
|
||||
/*
|
||||
* YP(+)--+-------------+
|
||||
* | |--+
|
||||
* | | |
|
||||
* YM(-)--+-------------+ |
|
||||
* +--------------+
|
||||
* | |
|
||||
* XP(open) XM(meas)
|
||||
*
|
||||
* (+) means here 1.85 V
|
||||
* (-) means here GND
|
||||
*/
|
||||
static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
writel(info[lradc->soc].mask,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(info[lradc->soc].y_plate,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
ts->cur_plate = LRADC_SAMPLE_Y;
|
||||
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
|
||||
mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
|
||||
}
|
||||
|
||||
/*
|
||||
* YP(+)--+-------------+
|
||||
* | |--+
|
||||
* | | |
|
||||
* YM(meas)--+-------------+ |
|
||||
* +--------------+
|
||||
* | |
|
||||
* XP(meas) XM(-)
|
||||
*
|
||||
* (+) means here 1.85 V
|
||||
* (-) means here GND
|
||||
*/
|
||||
static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
writel(info[lradc->soc].mask,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(info[lradc->soc].pressure,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
ts->cur_plate = LRADC_SAMPLE_PRESSURE;
|
||||
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
|
||||
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
|
||||
mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
|
||||
TOUCHSCREEN_VCHANNEL1);
|
||||
}
|
||||
|
||||
static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
mxs_lradc_setup_touch_detection(ts);
|
||||
|
||||
ts->cur_plate = LRADC_TOUCH;
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
/*
|
||||
* start with the Y-pos, because it uses nearly the same plate
|
||||
* settings like the touch detection
|
||||
*/
|
||||
mxs_lradc_prepare_y_pos(ts);
|
||||
}
|
||||
|
||||
static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
|
||||
input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
|
||||
input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
|
||||
input_report_key(ts->ts_input, BTN_TOUCH, 1);
|
||||
input_sync(ts->ts_input);
|
||||
}
|
||||
|
||||
static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
mxs_lradc_setup_touch_detection(ts);
|
||||
ts->cur_plate = LRADC_SAMPLE_VALID;
|
||||
/*
|
||||
* start a dummy conversion to burn time to settle the signals
|
||||
* note: we are not interested in the conversion's value
|
||||
*/
|
||||
writel(0, ts->base + LRADC_CH(TOUCHSCREEN_VCHANNEL1));
|
||||
writel(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10),
|
||||
ts->base + LRADC_DELAY(2));
|
||||
}
|
||||
|
||||
/*
|
||||
* in order to avoid false measurements, report only samples where
|
||||
* the surface is still touched after the position measurement
|
||||
*/
|
||||
static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
|
||||
{
|
||||
/* if it is still touched, report the sample */
|
||||
if (valid && mxs_lradc_check_touch_event(ts)) {
|
||||
ts->ts_valid = true;
|
||||
mxs_lradc_report_ts_event(ts);
|
||||
}
|
||||
|
||||
/* if it is even still touched, continue with the next measurement */
|
||||
if (mxs_lradc_check_touch_event(ts)) {
|
||||
mxs_lradc_prepare_y_pos(ts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ts->ts_valid) {
|
||||
/* signal the release */
|
||||
ts->ts_valid = false;
|
||||
input_report_key(ts->ts_input, BTN_TOUCH, 0);
|
||||
input_sync(ts->ts_input);
|
||||
}
|
||||
|
||||
/* if it is released, wait for the next touch via IRQ */
|
||||
ts->cur_plate = LRADC_TOUCH;
|
||||
writel(0, ts->base + LRADC_DELAY(2));
|
||||
writel(0, ts->base + LRADC_DELAY(3));
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ |
|
||||
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
/* touchscreen's state machine */
|
||||
static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
switch (ts->cur_plate) {
|
||||
case LRADC_TOUCH:
|
||||
if (mxs_lradc_check_touch_event(ts))
|
||||
mxs_lradc_start_touch_event(ts);
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
return;
|
||||
|
||||
case LRADC_SAMPLE_Y:
|
||||
ts->ts_y_pos =
|
||||
mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
|
||||
mxs_lradc_prepare_x_pos(ts);
|
||||
return;
|
||||
|
||||
case LRADC_SAMPLE_X:
|
||||
ts->ts_x_pos =
|
||||
mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
|
||||
mxs_lradc_prepare_pressure(ts);
|
||||
return;
|
||||
|
||||
case LRADC_SAMPLE_PRESSURE:
|
||||
ts->ts_pressure =
|
||||
mxs_lradc_read_ts_pressure(ts,
|
||||
TOUCHSCREEN_VCHANNEL2,
|
||||
TOUCHSCREEN_VCHANNEL1);
|
||||
mxs_lradc_complete_touch_event(ts);
|
||||
return;
|
||||
|
||||
case LRADC_SAMPLE_VALID:
|
||||
mxs_lradc_finish_touch_event(ts, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* IRQ Handling */
|
||||
static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct mxs_lradc_ts *ts = data;
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
unsigned long reg = readl(ts->base + LRADC_CTRL1);
|
||||
u32 clr_irq = mxs_lradc_irq_mask(lradc);
|
||||
const u32 ts_irq_mask =
|
||||
LRADC_CTRL1_TOUCH_DETECT_IRQ |
|
||||
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
|
||||
unsigned long flags;
|
||||
|
||||
if (!(reg & mxs_lradc_irq_mask(lradc)))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (reg & ts_irq_mask) {
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
mxs_lradc_handle_touch(ts);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
/* Make sure we don't clear the next conversion's interrupt. */
|
||||
clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
|
||||
writel(reg & clr_irq,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mxs_lradc_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct mxs_lradc_ts *ts = input_get_drvdata(dev);
|
||||
|
||||
/* Enable the touch-detect circuitry. */
|
||||
mxs_lradc_enable_touch_detection(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_stop(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
int i;
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
/* stop all interrupts from firing */
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
|
||||
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
|
||||
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2),
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* Power-down touchscreen touch-detect circuitry. */
|
||||
writel(info[lradc->soc].mask,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
|
||||
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
|
||||
writel(0, ts->base + LRADC_DELAY(i));
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_close(struct input_dev *dev)
|
||||
{
|
||||
struct mxs_lradc_ts *ts = input_get_drvdata(dev);
|
||||
|
||||
mxs_lradc_ts_stop(ts);
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct mxs_lradc *lradc = ts->lradc;
|
||||
|
||||
/* Configure the touchscreen type */
|
||||
if (lradc->soc == IMX28_LRADC) {
|
||||
writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
||||
writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
}
|
||||
|
||||
static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
|
||||
{
|
||||
struct input_dev *input = ts->ts_input;
|
||||
struct device *dev = ts->dev;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = "mxs-lradc-ts";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->open = mxs_lradc_ts_open;
|
||||
input->close = mxs_lradc_ts_close;
|
||||
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
|
||||
0, 0);
|
||||
|
||||
ts->ts_input = input;
|
||||
input_set_drvdata(input, ts);
|
||||
|
||||
return input_register_device(input);
|
||||
}
|
||||
|
||||
static int mxs_lradc_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->parent->of_node;
|
||||
struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
|
||||
struct mxs_lradc_ts *ts;
|
||||
struct resource *iores;
|
||||
int ret, irq, virq, i;
|
||||
u32 ts_wires = 0, adapt;
|
||||
|
||||
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
|
||||
ts->lradc = lradc;
|
||||
ts->dev = dev;
|
||||
spin_lock_init(&ts->lock);
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ts->base = devm_ioremap(dev, iores->start, resource_size(iores));
|
||||
if (IS_ERR(ts->base))
|
||||
return PTR_ERR(ts->base);
|
||||
|
||||
ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
|
||||
&ts_wires);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
|
||||
ts->over_sample_cnt = 4;
|
||||
} else {
|
||||
if (adapt >= 1 && adapt <= 32) {
|
||||
ts->over_sample_cnt = adapt;
|
||||
} else {
|
||||
dev_err(ts->dev, "Invalid sample count (%u)\n",
|
||||
adapt);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
|
||||
ts->over_sample_delay = 2;
|
||||
} else {
|
||||
if (adapt >= 2 && adapt <= LRADC_DELAY_DELAY_MASK + 1) {
|
||||
ts->over_sample_delay = adapt;
|
||||
} else {
|
||||
dev_err(ts->dev, "Invalid sample delay (%u)\n",
|
||||
adapt);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "fsl,settling", &adapt)) {
|
||||
ts->settling_delay = 10;
|
||||
} else {
|
||||
if (adapt >= 1 && adapt <= LRADC_DELAY_DELAY_MASK) {
|
||||
ts->settling_delay = adapt;
|
||||
} else {
|
||||
dev_err(ts->dev, "Invalid settling delay (%u)\n",
|
||||
adapt);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = stmp_reset_block(ts->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mxs_lradc_ts_hw_init(ts);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
irq = platform_get_irq_byname(pdev, mxs_lradc_ts_irq_names[i]);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
virq = irq_of_parse_and_map(node, irq);
|
||||
|
||||
mxs_lradc_ts_stop(ts);
|
||||
|
||||
ret = devm_request_irq(dev, virq,
|
||||
mxs_lradc_ts_handle_irq,
|
||||
0, mxs_lradc_ts_irq_names[i], ts);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return mxs_lradc_ts_register(ts);
|
||||
}
|
||||
|
||||
static struct platform_driver mxs_lradc_ts_driver = {
|
||||
.driver = {
|
||||
.name = "mxs-lradc-ts",
|
||||
},
|
||||
.probe = mxs_lradc_ts_probe,
|
||||
};
|
||||
module_platform_driver(mxs_lradc_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("Freescale MXS LRADC touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mxs-lradc-ts");
|
@ -30,6 +30,7 @@ config ATMEL_EBI
|
||||
default y
|
||||
depends on ARCH_AT91 && OF
|
||||
select MFD_SYSCON
|
||||
select MFD_ATMEL_SMC
|
||||
help
|
||||
Driver for Atmel EBI controller.
|
||||
Used to configure the EBI (external bus interface) when the device-
|
||||
|
@ -18,219 +18,157 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct at91sam9_smc_timings {
|
||||
u32 ncs_rd_setup_ns;
|
||||
u32 nrd_setup_ns;
|
||||
u32 ncs_wr_setup_ns;
|
||||
u32 nwe_setup_ns;
|
||||
u32 ncs_rd_pulse_ns;
|
||||
u32 nrd_pulse_ns;
|
||||
u32 ncs_wr_pulse_ns;
|
||||
u32 nwe_pulse_ns;
|
||||
u32 nrd_cycle_ns;
|
||||
u32 nwe_cycle_ns;
|
||||
u32 tdf_ns;
|
||||
};
|
||||
|
||||
struct at91sam9_smc_generic_fields {
|
||||
struct regmap_field *setup;
|
||||
struct regmap_field *pulse;
|
||||
struct regmap_field *cycle;
|
||||
struct regmap_field *mode;
|
||||
};
|
||||
|
||||
struct at91sam9_ebi_dev_config {
|
||||
struct at91sam9_smc_timings timings;
|
||||
u32 mode;
|
||||
};
|
||||
|
||||
struct at91_ebi_dev_config {
|
||||
struct atmel_ebi_dev_config {
|
||||
int cs;
|
||||
union {
|
||||
struct at91sam9_ebi_dev_config sam9;
|
||||
};
|
||||
struct atmel_smc_cs_conf smcconf;
|
||||
};
|
||||
|
||||
struct at91_ebi;
|
||||
struct atmel_ebi;
|
||||
|
||||
struct at91_ebi_dev {
|
||||
struct atmel_ebi_dev {
|
||||
struct list_head node;
|
||||
struct at91_ebi *ebi;
|
||||
struct atmel_ebi *ebi;
|
||||
u32 mode;
|
||||
int numcs;
|
||||
struct at91_ebi_dev_config configs[];
|
||||
struct atmel_ebi_dev_config configs[];
|
||||
};
|
||||
|
||||
struct at91_ebi_caps {
|
||||
struct atmel_ebi_caps {
|
||||
unsigned int available_cs;
|
||||
const struct reg_field *ebi_csa;
|
||||
void (*get_config)(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*xlate_config)(struct at91_ebi_dev *ebid,
|
||||
unsigned int ebi_csa_offs;
|
||||
void (*get_config)(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf);
|
||||
int (*xlate_config)(struct atmel_ebi_dev *ebid,
|
||||
struct device_node *configs_np,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*apply_config)(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*init)(struct at91_ebi *ebi);
|
||||
struct atmel_ebi_dev_config *conf);
|
||||
void (*apply_config)(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf);
|
||||
};
|
||||
|
||||
struct at91_ebi {
|
||||
struct atmel_ebi {
|
||||
struct clk *clk;
|
||||
struct regmap *matrix;
|
||||
struct {
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
} smc;
|
||||
struct regmap_field *ebi_csa;
|
||||
|
||||
struct device *dev;
|
||||
const struct at91_ebi_caps *caps;
|
||||
const struct atmel_ebi_caps *caps;
|
||||
struct list_head devs;
|
||||
union {
|
||||
struct at91sam9_smc_generic_fields sam9;
|
||||
};
|
||||
};
|
||||
|
||||
static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
struct atmel_smc_timing_xlate {
|
||||
const char *name;
|
||||
int (*converter)(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int nycles);
|
||||
unsigned int shift;
|
||||
};
|
||||
|
||||
#define ATMEL_SMC_SETUP_XLATE(nm, pos) \
|
||||
{ .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos}
|
||||
|
||||
#define ATMEL_SMC_PULSE_XLATE(nm, pos) \
|
||||
{ .name = nm, .converter = atmel_smc_cs_conf_set_pulse, .shift = pos}
|
||||
|
||||
#define ATMEL_SMC_CYCLE_XLATE(nm, pos) \
|
||||
{ .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos}
|
||||
|
||||
static void at91sam9_ebi_get_config(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
|
||||
unsigned int clk_period = NSEC_PER_SEC / clk_get_rate(ebid->ebi->clk);
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
struct at91sam9_smc_timings *timings = &config->timings;
|
||||
unsigned int val;
|
||||
|
||||
regmap_fields_read(fields->mode, conf->cs, &val);
|
||||
config->mode = val & ~AT91_SMC_TDF;
|
||||
|
||||
val = (val & AT91_SMC_TDF) >> 16;
|
||||
timings->tdf_ns = clk_period * val;
|
||||
|
||||
regmap_fields_read(fields->setup, conf->cs, &val);
|
||||
timings->ncs_rd_setup_ns = (val >> 24) & 0x1f;
|
||||
timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128;
|
||||
timings->ncs_rd_setup_ns *= clk_period;
|
||||
timings->nrd_setup_ns = (val >> 16) & 0x1f;
|
||||
timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128;
|
||||
timings->nrd_setup_ns *= clk_period;
|
||||
timings->ncs_wr_setup_ns = (val >> 8) & 0x1f;
|
||||
timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128;
|
||||
timings->ncs_wr_setup_ns *= clk_period;
|
||||
timings->nwe_setup_ns = val & 0x1f;
|
||||
timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128;
|
||||
timings->nwe_setup_ns *= clk_period;
|
||||
|
||||
regmap_fields_read(fields->pulse, conf->cs, &val);
|
||||
timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f;
|
||||
timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256;
|
||||
timings->ncs_rd_pulse_ns *= clk_period;
|
||||
timings->nrd_pulse_ns = (val >> 16) & 0x3f;
|
||||
timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256;
|
||||
timings->nrd_pulse_ns *= clk_period;
|
||||
timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f;
|
||||
timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256;
|
||||
timings->ncs_wr_pulse_ns *= clk_period;
|
||||
timings->nwe_pulse_ns = val & 0x3f;
|
||||
timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256;
|
||||
timings->nwe_pulse_ns *= clk_period;
|
||||
|
||||
regmap_fields_read(fields->cycle, conf->cs, &val);
|
||||
timings->nrd_cycle_ns = (val >> 16) & 0x7f;
|
||||
timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256;
|
||||
timings->nrd_cycle_ns *= clk_period;
|
||||
timings->nwe_cycle_ns = val & 0x7f;
|
||||
timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256;
|
||||
timings->nwe_cycle_ns *= clk_period;
|
||||
atmel_smc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs,
|
||||
&conf->smcconf);
|
||||
}
|
||||
|
||||
static int at91_xlate_timing(struct device_node *np, const char *prop,
|
||||
u32 *val, bool *required)
|
||||
static void sama5_ebi_get_config(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf)
|
||||
{
|
||||
if (!of_property_read_u32(np, prop, val)) {
|
||||
*required = true;
|
||||
return 0;
|
||||
atmel_hsmc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs,
|
||||
&conf->smcconf);
|
||||
}
|
||||
|
||||
static const struct atmel_smc_timing_xlate timings_xlate_table[] = {
|
||||
ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-rd-setup-ns",
|
||||
ATMEL_SMC_NCS_RD_SHIFT),
|
||||
ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-wr-setup-ns",
|
||||
ATMEL_SMC_NCS_WR_SHIFT),
|
||||
ATMEL_SMC_SETUP_XLATE("atmel,smc-nrd-setup-ns", ATMEL_SMC_NRD_SHIFT),
|
||||
ATMEL_SMC_SETUP_XLATE("atmel,smc-nwe-setup-ns", ATMEL_SMC_NWE_SHIFT),
|
||||
ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-rd-pulse-ns",
|
||||
ATMEL_SMC_NCS_RD_SHIFT),
|
||||
ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-wr-pulse-ns",
|
||||
ATMEL_SMC_NCS_WR_SHIFT),
|
||||
ATMEL_SMC_PULSE_XLATE("atmel,smc-nrd-pulse-ns", ATMEL_SMC_NRD_SHIFT),
|
||||
ATMEL_SMC_PULSE_XLATE("atmel,smc-nwe-pulse-ns", ATMEL_SMC_NWE_SHIFT),
|
||||
ATMEL_SMC_CYCLE_XLATE("atmel,smc-nrd-cycle-ns", ATMEL_SMC_NRD_SHIFT),
|
||||
ATMEL_SMC_CYCLE_XLATE("atmel,smc-nwe-cycle-ns", ATMEL_SMC_NWE_SHIFT),
|
||||
};
|
||||
|
||||
static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct atmel_smc_cs_conf *smcconf)
|
||||
{
|
||||
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
|
||||
unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate;
|
||||
bool required = false;
|
||||
unsigned int ncycles;
|
||||
int ret, i;
|
||||
u32 val;
|
||||
|
||||
ret = of_property_read_u32(np, "atmel,smc-tdf-ns", &val);
|
||||
if (!ret) {
|
||||
required = true;
|
||||
ncycles = DIV_ROUND_UP(val, clk_period_ns);
|
||||
if (ncycles > ATMEL_SMC_MODE_TDF_MAX ||
|
||||
ncycles < ATMEL_SMC_MODE_TDF_MIN) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles);
|
||||
}
|
||||
|
||||
if (*required)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(timings_xlate_table); i++) {
|
||||
const struct atmel_smc_timing_xlate *xlate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
xlate = &timings_xlate_table[i];
|
||||
|
||||
static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct at91sam9_smc_timings *timings,
|
||||
bool *required)
|
||||
{
|
||||
int ret;
|
||||
ret = of_property_read_u32(np, xlate->name, &val);
|
||||
if (ret) {
|
||||
if (!required)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns",
|
||||
&timings->ncs_rd_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!required) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns",
|
||||
&timings->nrd_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns",
|
||||
&timings->ncs_wr_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns",
|
||||
&timings->nwe_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns",
|
||||
&timings->ncs_rd_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns",
|
||||
&timings->nrd_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns",
|
||||
&timings->ncs_wr_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns",
|
||||
&timings->nwe_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns",
|
||||
&timings->nwe_cycle_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns",
|
||||
&timings->nrd_cycle_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-tdf-ns",
|
||||
&timings->tdf_ns, required);
|
||||
ncycles = DIV_ROUND_UP(val, clk_period_ns);
|
||||
ret = xlate->converter(smcconf, xlate->shift, ncycles);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(ebid->ebi->dev,
|
||||
"missing or invalid timings definition in %s",
|
||||
np->full_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return required;
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct atmel_ebi_dev_config *conf)
|
||||
{
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
struct atmel_smc_cs_conf *smcconf = &conf->smcconf;
|
||||
bool required = false;
|
||||
const char *tmp_str;
|
||||
u32 tmp;
|
||||
@ -240,15 +178,15 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
if (!ret) {
|
||||
switch (tmp) {
|
||||
case 8:
|
||||
config->mode |= AT91_SMC_DBW_8;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_DBW_8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
config->mode |= AT91_SMC_DBW_16;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
config->mode |= AT91_SMC_DBW_32;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_DBW_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -259,28 +197,28 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
|
||||
config->mode |= AT91_SMC_TDFMODE_OPTIMIZED;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "write")) {
|
||||
config->mode |= AT91_SMC_BAT_WRITE;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_BAT_WRITE;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-read-mode", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "nrd")) {
|
||||
config->mode |= AT91_SMC_READMODE_NRD;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-write-mode", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "nwe")) {
|
||||
config->mode |= AT91_SMC_WRITEMODE_NWE;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_WRITEMODE_NWE;
|
||||
required = true;
|
||||
}
|
||||
|
||||
@ -288,9 +226,9 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str);
|
||||
if (tmp_str) {
|
||||
if (!strcmp(tmp_str, "frozen"))
|
||||
config->mode |= AT91_SMC_EXNWMODE_FROZEN;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_FROZEN;
|
||||
else if (!strcmp(tmp_str, "ready"))
|
||||
config->mode |= AT91_SMC_EXNWMODE_READY;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_READY;
|
||||
else if (strcmp(tmp_str, "disabled"))
|
||||
return -EINVAL;
|
||||
|
||||
@ -301,164 +239,63 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
if (!ret) {
|
||||
switch (tmp) {
|
||||
case 4:
|
||||
config->mode |= AT91_SMC_PS_4;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_PS_4;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
config->mode |= AT91_SMC_PS_8;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_PS_8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
config->mode |= AT91_SMC_PS_16;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_PS_16;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
config->mode |= AT91_SMC_PS_32;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_PS_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->mode |= AT91_SMC_PMEN;
|
||||
smcconf->mode |= ATMEL_SMC_MODE_PMEN;
|
||||
required = true;
|
||||
}
|
||||
|
||||
ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings,
|
||||
&required);
|
||||
ret = atmel_ebi_xslate_smc_timings(ebid, np, &conf->smcconf);
|
||||
if (ret)
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
|
||||
if ((ret > 0 && !required) || (!ret && required)) {
|
||||
dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %s",
|
||||
np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
static void at91sam9_ebi_apply_config(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf)
|
||||
{
|
||||
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
|
||||
unsigned int clk_period = NSEC_PER_SEC / clk_rate;
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
struct at91sam9_smc_timings *timings = &config->timings;
|
||||
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
|
||||
u32 coded_val;
|
||||
u32 val;
|
||||
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->ncs_rd_setup_ns);
|
||||
val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->nrd_setup_ns);
|
||||
val |= AT91SAM9_SMC_NRDSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->ncs_wr_setup_ns);
|
||||
val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->nwe_setup_ns);
|
||||
val |= AT91SAM9_SMC_NWESETUP(coded_val);
|
||||
regmap_fields_write(fields->setup, conf->cs, val);
|
||||
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->ncs_rd_pulse_ns);
|
||||
val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->nrd_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NRDPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->ncs_wr_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->nwe_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NWEPULSE(coded_val);
|
||||
regmap_fields_write(fields->pulse, conf->cs, val);
|
||||
|
||||
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
|
||||
timings->nrd_cycle_ns);
|
||||
val = AT91SAM9_SMC_NRDCYCLE(coded_val);
|
||||
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
|
||||
timings->nwe_cycle_ns);
|
||||
val |= AT91SAM9_SMC_NWECYCLE(coded_val);
|
||||
regmap_fields_write(fields->cycle, conf->cs, val);
|
||||
|
||||
val = DIV_ROUND_UP(timings->tdf_ns, clk_period);
|
||||
if (val > AT91_SMC_TDF_MAX)
|
||||
val = AT91_SMC_TDF_MAX;
|
||||
regmap_fields_write(fields->mode, conf->cs,
|
||||
config->mode | AT91_SMC_TDF_(val));
|
||||
|
||||
return 0;
|
||||
atmel_smc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs,
|
||||
&conf->smcconf);
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_init(struct at91_ebi *ebi)
|
||||
static void sama5_ebi_apply_config(struct atmel_ebi_dev *ebid,
|
||||
struct atmel_ebi_dev_config *conf)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
|
||||
struct reg_field field = REG_FIELD(0, 0, 31);
|
||||
|
||||
field.id_size = fls(ebi->caps->available_cs);
|
||||
field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ;
|
||||
|
||||
field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC);
|
||||
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->setup))
|
||||
return PTR_ERR(fields->setup);
|
||||
|
||||
field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC);
|
||||
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->pulse))
|
||||
return PTR_ERR(fields->pulse);
|
||||
|
||||
field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC);
|
||||
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->cycle))
|
||||
return PTR_ERR(fields->cycle);
|
||||
|
||||
field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
|
||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
return PTR_ERR_OR_ZERO(fields->mode);
|
||||
atmel_hsmc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs,
|
||||
&conf->smcconf);
|
||||
}
|
||||
|
||||
static int sama5d3_ebi_init(struct at91_ebi *ebi)
|
||||
static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
|
||||
int reg_cells)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
|
||||
struct reg_field field = REG_FIELD(0, 0, 31);
|
||||
|
||||
field.id_size = fls(ebi->caps->available_cs);
|
||||
field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ;
|
||||
|
||||
field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC);
|
||||
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->setup))
|
||||
return PTR_ERR(fields->setup);
|
||||
|
||||
field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC);
|
||||
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->pulse))
|
||||
return PTR_ERR(fields->pulse);
|
||||
|
||||
field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC);
|
||||
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
if (IS_ERR(fields->cycle))
|
||||
return PTR_ERR(fields->cycle);
|
||||
|
||||
field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
|
||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap,
|
||||
field);
|
||||
return PTR_ERR_OR_ZERO(fields->mode);
|
||||
}
|
||||
|
||||
static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
int reg_cells)
|
||||
{
|
||||
const struct at91_ebi_caps *caps = ebi->caps;
|
||||
struct at91_ebi_dev_config conf = { };
|
||||
const struct atmel_ebi_caps *caps = ebi->caps;
|
||||
struct atmel_ebi_dev_config conf = { };
|
||||
struct device *dev = ebi->dev;
|
||||
struct at91_ebi_dev *ebid;
|
||||
struct atmel_ebi_dev *ebid;
|
||||
unsigned long cslines = 0;
|
||||
int ret, numcs = 0, nentries, i;
|
||||
bool apply = false;
|
||||
@ -495,6 +332,7 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
return -ENOMEM;
|
||||
|
||||
ebid->ebi = ebi;
|
||||
ebid->numcs = numcs;
|
||||
|
||||
ret = caps->xlate_config(ebid, np, &conf);
|
||||
if (ret < 0)
|
||||
@ -508,9 +346,7 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
|
||||
if (apply) {
|
||||
conf.cs = cs;
|
||||
ret = caps->apply_config(ebid, &conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
caps->apply_config(ebid, &conf);
|
||||
}
|
||||
|
||||
caps->get_config(ebid, &ebid->configs[i]);
|
||||
@ -519,9 +355,10 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
* Attach the EBI device to the generic SMC logic if at least
|
||||
* one "atmel,smc-" property is present.
|
||||
*/
|
||||
if (ebi->ebi_csa && apply)
|
||||
regmap_field_update_bits(ebi->ebi_csa,
|
||||
BIT(cs), 0);
|
||||
if (ebi->caps->ebi_csa_offs && apply)
|
||||
regmap_update_bits(ebi->matrix,
|
||||
ebi->caps->ebi_csa_offs,
|
||||
BIT(cs), 0);
|
||||
|
||||
i++;
|
||||
}
|
||||
@ -531,102 +368,70 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reg_field at91sam9260_ebi_csa =
|
||||
REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9260_ebi_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9260_ebi_caps = {
|
||||
.available_cs = 0xff,
|
||||
.ebi_csa = &at91sam9260_ebi_csa,
|
||||
.ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9261_ebi_csa =
|
||||
REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9261_ebi_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9261_ebi_caps = {
|
||||
.available_cs = 0xff,
|
||||
.ebi_csa = &at91sam9261_ebi_csa,
|
||||
.ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9263_ebi0_csa =
|
||||
REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9263_ebi0_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9263_ebi0_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9263_ebi0_csa,
|
||||
.ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9263_ebi1_csa =
|
||||
REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9263_ebi1_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9263_ebi1_caps = {
|
||||
.available_cs = 0x7,
|
||||
.ebi_csa = &at91sam9263_ebi1_csa,
|
||||
.ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9rl_ebi_csa =
|
||||
REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9rl_ebi_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9rl_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9rl_ebi_csa,
|
||||
.ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9g45_ebi_csa =
|
||||
REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9g45_ebi_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9g45_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9g45_ebi_csa,
|
||||
.ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct at91_ebi_caps at91sam9x5_ebi_caps = {
|
||||
static const struct atmel_ebi_caps at91sam9x5_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9263_ebi0_csa,
|
||||
.ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct at91_ebi_caps sama5d3_ebi_caps = {
|
||||
static const struct atmel_ebi_caps sama5d3_ebi_caps = {
|
||||
.available_cs = 0xf,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = sama5d3_ebi_init,
|
||||
.get_config = sama5_ebi_get_config,
|
||||
.xlate_config = atmel_ebi_xslate_smc_config,
|
||||
.apply_config = sama5_ebi_apply_config,
|
||||
};
|
||||
|
||||
static const struct of_device_id at91_ebi_id_table[] = {
|
||||
static const struct of_device_id atmel_ebi_id_table[] = {
|
||||
{
|
||||
.compatible = "atmel,at91sam9260-ebi",
|
||||
.data = &at91sam9260_ebi_caps,
|
||||
@ -662,7 +467,7 @@ static const struct of_device_id at91_ebi_id_table[] = {
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np)
|
||||
static int atmel_ebi_dev_disable(struct atmel_ebi *ebi, struct device_node *np)
|
||||
{
|
||||
struct device *dev = ebi->dev;
|
||||
struct property *newprop;
|
||||
@ -684,17 +489,17 @@ static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np)
|
||||
return of_update_property(np, newprop);
|
||||
}
|
||||
|
||||
static int at91_ebi_probe(struct platform_device *pdev)
|
||||
static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node, *smc_np;
|
||||
const struct of_device_id *match;
|
||||
struct at91_ebi *ebi;
|
||||
struct atmel_ebi *ebi;
|
||||
int ret, reg_cells;
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
|
||||
match = of_match_device(at91_ebi_id_table, dev);
|
||||
match = of_match_device(atmel_ebi_id_table, dev);
|
||||
if (!match || !match->data)
|
||||
return -EINVAL;
|
||||
|
||||
@ -702,6 +507,8 @@ static int at91_ebi_probe(struct platform_device *pdev)
|
||||
if (!ebi)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ebi);
|
||||
|
||||
INIT_LIST_HEAD(&ebi->devs);
|
||||
ebi->caps = match->data;
|
||||
ebi->dev = dev;
|
||||
@ -733,22 +540,13 @@ static int at91_ebi_probe(struct platform_device *pdev)
|
||||
* The sama5d3 does not provide an EBICSA register and thus does need
|
||||
* to access the matrix registers.
|
||||
*/
|
||||
if (ebi->caps->ebi_csa) {
|
||||
if (ebi->caps->ebi_csa_offs) {
|
||||
ebi->matrix =
|
||||
syscon_regmap_lookup_by_phandle(np, "atmel,matrix");
|
||||
if (IS_ERR(ebi->matrix))
|
||||
return PTR_ERR(ebi->matrix);
|
||||
|
||||
ebi->ebi_csa = regmap_field_alloc(ebi->matrix,
|
||||
*ebi->caps->ebi_csa);
|
||||
if (IS_ERR(ebi->ebi_csa))
|
||||
return PTR_ERR(ebi->ebi_csa);
|
||||
}
|
||||
|
||||
ret = ebi->caps->init(ebi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32(np, "#address-cells", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing #address-cells property\n");
|
||||
@ -769,12 +567,12 @@ static int at91_ebi_probe(struct platform_device *pdev)
|
||||
if (!of_find_property(child, "reg", NULL))
|
||||
continue;
|
||||
|
||||
ret = at91_ebi_dev_setup(ebi, child, reg_cells);
|
||||
ret = atmel_ebi_dev_setup(ebi, child, reg_cells);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to configure EBI bus for %s, disabling the device",
|
||||
child->full_name);
|
||||
|
||||
ret = at91_ebi_dev_disable(ebi, child);
|
||||
ret = atmel_ebi_dev_disable(ebi, child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -783,10 +581,28 @@ static int at91_ebi_probe(struct platform_device *pdev)
|
||||
return of_platform_populate(np, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
static struct platform_driver at91_ebi_driver = {
|
||||
static int atmel_ebi_resume(struct device *dev)
|
||||
{
|
||||
struct atmel_ebi *ebi = dev_get_drvdata(dev);
|
||||
struct atmel_ebi_dev *ebid;
|
||||
|
||||
list_for_each_entry(ebid, &ebi->devs, node) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ebid->numcs; i++)
|
||||
ebid->ebi->caps->apply_config(ebid, &ebid->configs[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume);
|
||||
|
||||
static struct platform_driver atmel_ebi_driver = {
|
||||
.driver = {
|
||||
.name = "atmel-ebi",
|
||||
.of_match_table = at91_ebi_id_table,
|
||||
.of_match_table = atmel_ebi_id_table,
|
||||
.pm = &atmel_ebi_pm_ops,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe);
|
||||
builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe);
|
||||
|
@ -121,6 +121,10 @@ config MFD_ATMEL_HLCDC
|
||||
additional drivers must be enabled in order to use the
|
||||
functionality of the device.
|
||||
|
||||
config MFD_ATMEL_SMC
|
||||
bool
|
||||
select MFD_SYSCON
|
||||
|
||||
config MFD_BCM590XX
|
||||
tristate "Broadcom BCM590xx PMUs"
|
||||
select MFD_CORE
|
||||
@ -263,13 +267,14 @@ config MFD_DA9055
|
||||
called "da9055"
|
||||
|
||||
config MFD_DA9062
|
||||
tristate "Dialog Semiconductor DA9062 PMIC Support"
|
||||
tristate "Dialog Semiconductor DA9062/61 PMIC Support"
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here for support for the Dialog Semiconductor DA9062 PMIC.
|
||||
Say yes here for support for the Dialog Semiconductor DA9061 and
|
||||
DA9062 PMICs.
|
||||
This includes the I2C driver and core APIs.
|
||||
Additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
@ -344,6 +349,23 @@ config MFD_MC13XXX_I2C
|
||||
help
|
||||
Select this if your MC13xxx is connected via an I2C bus.
|
||||
|
||||
config MFD_MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on ARCH_MXS || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
select STMP_DEVICE
|
||||
help
|
||||
Say yes here to build support for the Low Resolution
|
||||
Analog-to-Digital Converter (LRADC) found on the i.MX23 and i.MX28
|
||||
processors. This driver provides common support for accessing the
|
||||
device, additional drivers must be enabled in order to use the
|
||||
functionality of the device:
|
||||
mxs-lradc-adc for ADC readings
|
||||
mxs-lradc-ts for touchscreen support
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mxs-lradc.
|
||||
|
||||
config MFD_MX25_TSADC
|
||||
tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
|
||||
select REGMAP_MMIO
|
||||
@ -425,18 +447,29 @@ config LPC_SCH
|
||||
System Management Bus and General Purpose I/O.
|
||||
|
||||
config INTEL_SOC_PMIC
|
||||
bool "Support for Intel Atom SoC PMIC"
|
||||
bool "Support for Crystal Cove PMIC"
|
||||
depends on GPIOLIB
|
||||
depends on I2C=y
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Select this option to enable support for the PMIC device
|
||||
Select this option to enable support for Crystal Cove PMIC
|
||||
on some Intel SoC systems. The PMIC provides ADC, GPIO,
|
||||
thermal, charger and related power management functions
|
||||
on these systems.
|
||||
|
||||
config INTEL_SOC_PMIC_BXTWC
|
||||
tristate "Support for Intel Broxton Whiskey Cove PMIC"
|
||||
depends on INTEL_PMC_IPC
|
||||
select MFD_CORE
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Select this option to enable support for Whiskey Cove PMIC
|
||||
on Intel Broxton systems. The PMIC provides ADC, GPIO,
|
||||
thermal, charger and related power management functions
|
||||
on these systems.
|
||||
|
||||
config MFD_INTEL_LPSS
|
||||
tristate
|
||||
select COMMON_CLK
|
||||
@ -1164,6 +1197,18 @@ config MFD_LP8788
|
||||
TI LP8788 PMU supports regulators, battery charger, RTC,
|
||||
ADC, backlight driver and current sinks.
|
||||
|
||||
config MFD_TI_LMU
|
||||
tristate "TI Lighting Management Unit driver"
|
||||
depends on I2C
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to enable support for TI LMU chips.
|
||||
|
||||
TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.
|
||||
It consists of backlight, LED and regulator driver.
|
||||
It provides consistent device controls for lighting functions.
|
||||
|
||||
config MFD_OMAP_USB_HOST
|
||||
bool "TI OMAP USBHS core and TLL driver"
|
||||
depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3
|
||||
|
@ -10,7 +10,9 @@ obj-$(CONFIG_MFD_ACT8945A) += act8945a.o
|
||||
obj-$(CONFIG_MFD_SM501) += sm501.o
|
||||
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
|
||||
obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
|
||||
cros_ec_core-objs := cros_ec.o
|
||||
cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o
|
||||
obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o
|
||||
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
|
||||
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
|
||||
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
|
||||
@ -125,6 +127,8 @@ obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
|
||||
obj-$(CONFIG_MFD_LP3943) += lp3943.o
|
||||
obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_TI_LMU) += ti-lmu.o
|
||||
|
||||
da9055-objs := da9055-core.o da9055-i2c.o
|
||||
obj-$(CONFIG_MFD_DA9055) += da9055.o
|
||||
obj-$(CONFIG_MFD_DA9062) += da9062-core.o
|
||||
@ -181,6 +185,7 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o
|
||||
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
|
||||
obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
|
||||
obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
|
||||
obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
|
||||
@ -207,11 +212,12 @@ obj-$(CONFIG_MFD_RT5033) += rt5033.o
|
||||
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
|
||||
|
||||
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
|
||||
intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o
|
||||
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
|
||||
obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o
|
||||
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
|
||||
|
||||
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
|
||||
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
|
||||
|
||||
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
|
||||
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
|
||||
|
@ -33,6 +33,10 @@ static const struct mfd_cell altr_a10sr_subdev_info[] = {
|
||||
.name = "altr_a10sr_gpio",
|
||||
.of_compatible = "altr,a10sr-gpio",
|
||||
},
|
||||
{
|
||||
.name = "altr_a10sr_reset",
|
||||
.of_compatible = "altr,a10sr-reset",
|
||||
},
|
||||
};
|
||||
|
||||
static bool altr_a10sr_reg_readable(struct device *dev, unsigned int reg)
|
||||
|
@ -235,29 +235,25 @@ static irqreturn_t arizona_overclocked(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define ARIZONA_REG_POLL_DELAY_US 7500
|
||||
|
||||
static int arizona_poll_reg(struct arizona *arizona,
|
||||
int timeout, unsigned int reg,
|
||||
int timeout_ms, unsigned int reg,
|
||||
unsigned int mask, unsigned int target)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < timeout; i++) {
|
||||
ret = regmap_read(arizona->regmap, reg, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read reg %u: %d\n",
|
||||
reg, ret);
|
||||
continue;
|
||||
}
|
||||
ret = regmap_read_poll_timeout(arizona->regmap,
|
||||
ARIZONA_INTERRUPT_RAW_STATUS_5, val,
|
||||
((val & mask) == target),
|
||||
ARIZONA_REG_POLL_DELAY_US,
|
||||
timeout_ms * 1000);
|
||||
if (ret)
|
||||
dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n",
|
||||
reg, val);
|
||||
|
||||
if ((val & mask) == target)
|
||||
return 0;
|
||||
|
||||
usleep_range(1000, 5000);
|
||||
}
|
||||
|
||||
dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val);
|
||||
return -ETIMEDOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arizona_wait_for_boot(struct arizona *arizona)
|
||||
@ -269,7 +265,7 @@ static int arizona_wait_for_boot(struct arizona *arizona)
|
||||
* we won't race with the interrupt handler as it'll be blocked on
|
||||
* runtime resume.
|
||||
*/
|
||||
ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5,
|
||||
ret = arizona_poll_reg(arizona, 30, ARIZONA_INTERRUPT_RAW_STATUS_5,
|
||||
ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS);
|
||||
|
||||
if (!ret)
|
||||
@ -339,13 +335,11 @@ static int arizona_enable_freerun_sysclk(struct arizona *arizona,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
|
||||
ret = arizona_poll_reg(arizona, 180, ARIZONA_INTERRUPT_RAW_STATUS_5,
|
||||
ARIZONA_FLL1_CLOCK_OK_STS,
|
||||
ARIZONA_FLL1_CLOCK_OK_STS);
|
||||
if (ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret)
|
||||
goto err_fll;
|
||||
}
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
|
||||
if (ret) {
|
||||
@ -405,13 +399,11 @@ static int wm5102_apply_hardware_patch(struct arizona *arizona)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
|
||||
ret = arizona_poll_reg(arizona, 30, ARIZONA_WRITE_SEQUENCER_CTRL_1,
|
||||
ARIZONA_WSEQ_BUSY, 0);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
|
||||
ARIZONA_WSEQ_ABORT);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err:
|
||||
err = arizona_disable_freerun_sysclk(arizona, &state);
|
||||
|
@ -78,7 +78,7 @@ struct asic3 {
|
||||
unsigned int bus_shift;
|
||||
unsigned int irq_nr;
|
||||
unsigned int irq_base;
|
||||
spinlock_t lock;
|
||||
raw_spinlock_t lock;
|
||||
u16 irq_bothedge[4];
|
||||
struct gpio_chip gpio;
|
||||
struct device *dev;
|
||||
@ -108,14 +108,14 @@ static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set)
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
val = asic3_read_register(asic, reg);
|
||||
if (set)
|
||||
val |= bits;
|
||||
else
|
||||
val &= ~bits;
|
||||
asic3_write_register(asic, reg, val);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
/* IRQs */
|
||||
@ -129,13 +129,13 @@ static void asic3_irq_flip_edge(struct asic3 *asic,
|
||||
u16 edge;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
edge = asic3_read_register(asic,
|
||||
base + ASIC3_GPIO_EDGE_TRIGGER);
|
||||
edge ^= bit;
|
||||
asic3_write_register(asic,
|
||||
base + ASIC3_GPIO_EDGE_TRIGGER, edge);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static void asic3_irq_demux(struct irq_desc *desc)
|
||||
@ -151,10 +151,10 @@ static void asic3_irq_demux(struct irq_desc *desc)
|
||||
u32 status;
|
||||
int bank;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
status = asic3_read_register(asic,
|
||||
ASIC3_OFFSET(INTR, P_INT_STAT));
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
|
||||
/* Check all ten register bits */
|
||||
if ((status & 0x3ff) == 0)
|
||||
@ -167,7 +167,7 @@ static void asic3_irq_demux(struct irq_desc *desc)
|
||||
|
||||
base = ASIC3_GPIO_A_BASE
|
||||
+ bank * ASIC3_GPIO_BASE_INCR;
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
istat = asic3_read_register(asic,
|
||||
base +
|
||||
ASIC3_GPIO_INT_STATUS);
|
||||
@ -175,7 +175,7 @@ static void asic3_irq_demux(struct irq_desc *desc)
|
||||
asic3_write_register(asic,
|
||||
base +
|
||||
ASIC3_GPIO_INT_STATUS, 0);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
|
||||
for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) {
|
||||
int bit = (1 << i);
|
||||
@ -230,11 +230,11 @@ static void asic3_mask_gpio_irq(struct irq_data *data)
|
||||
bank = asic3_irq_to_bank(asic, data->irq);
|
||||
index = asic3_irq_to_index(asic, data->irq);
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
|
||||
val |= 1 << index;
|
||||
asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static void asic3_mask_irq(struct irq_data *data)
|
||||
@ -243,7 +243,7 @@ static void asic3_mask_irq(struct irq_data *data)
|
||||
int regval;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
regval = asic3_read_register(asic,
|
||||
ASIC3_INTR_BASE +
|
||||
ASIC3_INTR_INT_MASK);
|
||||
@ -255,7 +255,7 @@ static void asic3_mask_irq(struct irq_data *data)
|
||||
ASIC3_INTR_BASE +
|
||||
ASIC3_INTR_INT_MASK,
|
||||
regval);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static void asic3_unmask_gpio_irq(struct irq_data *data)
|
||||
@ -267,11 +267,11 @@ static void asic3_unmask_gpio_irq(struct irq_data *data)
|
||||
bank = asic3_irq_to_bank(asic, data->irq);
|
||||
index = asic3_irq_to_index(asic, data->irq);
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
|
||||
val &= ~(1 << index);
|
||||
asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static void asic3_unmask_irq(struct irq_data *data)
|
||||
@ -280,7 +280,7 @@ static void asic3_unmask_irq(struct irq_data *data)
|
||||
int regval;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
regval = asic3_read_register(asic,
|
||||
ASIC3_INTR_BASE +
|
||||
ASIC3_INTR_INT_MASK);
|
||||
@ -292,7 +292,7 @@ static void asic3_unmask_irq(struct irq_data *data)
|
||||
ASIC3_INTR_BASE +
|
||||
ASIC3_INTR_INT_MASK,
|
||||
regval);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
|
||||
@ -306,7 +306,7 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
|
||||
index = asic3_irq_to_index(asic, data->irq);
|
||||
bit = 1<<index;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
level = asic3_read_register(asic,
|
||||
bank + ASIC3_GPIO_LEVEL_TRIGGER);
|
||||
edge = asic3_read_register(asic,
|
||||
@ -348,7 +348,7 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
|
||||
edge);
|
||||
asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE,
|
||||
trigger);
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
|
||||
out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION);
|
||||
|
||||
@ -467,7 +467,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip,
|
||||
|
||||
asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg);
|
||||
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -524,7 +524,7 @@ static void asic3_gpio_set(struct gpio_chip *chip,
|
||||
|
||||
mask = ASIC3_GPIO_TO_MASK(offset);
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
|
||||
out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT);
|
||||
|
||||
@ -535,7 +535,7 @@ static void asic3_gpio_set(struct gpio_chip *chip,
|
||||
|
||||
asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg);
|
||||
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
@ -611,13 +611,13 @@ static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
unsigned long flags;
|
||||
u32 cdex;
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
if (clk->enabled++ == 0) {
|
||||
cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
|
||||
cdex |= clk->cdex;
|
||||
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
|
||||
}
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
@ -627,13 +627,13 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
|
||||
WARN_ON(clk->enabled == 0);
|
||||
|
||||
spin_lock_irqsave(&asic->lock, flags);
|
||||
raw_spin_lock_irqsave(&asic->lock, flags);
|
||||
if (--clk->enabled == 0) {
|
||||
cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
|
||||
cdex &= ~clk->cdex;
|
||||
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
|
||||
}
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&asic->lock, flags);
|
||||
}
|
||||
|
||||
/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
|
||||
@ -963,7 +963,7 @@ static int __init asic3_probe(struct platform_device *pdev)
|
||||
if (!asic)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&asic->lock);
|
||||
raw_spin_lock_init(&asic->lock);
|
||||
platform_set_drvdata(pdev, asic);
|
||||
asic->dev = &pdev->dev;
|
||||
|
||||
|
314
drivers/mfd/atmel-smc.c
Normal file
314
drivers/mfd/atmel-smc.c
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Atmel SMC (Static Memory Controller) helper functions.
|
||||
*
|
||||
* Copyright (C) 2017 Atmel
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon/atmel-smc.h>
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_init - initialize a SMC CS conf
|
||||
* @conf: the SMC CS conf to initialize
|
||||
*
|
||||
* Set all fields to 0 so that one can start defining a new config.
|
||||
*/
|
||||
void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
|
||||
{
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
|
||||
* format expected by the SMC engine
|
||||
* @ncycles: number of MCK clk cycles
|
||||
* @msbpos: position of the MSB part of the timing field
|
||||
* @msbwidth: width of the MSB part of the timing field
|
||||
* @msbfactor: factor applied to the MSB
|
||||
* @encodedval: param used to store the encoding result
|
||||
*
|
||||
* This function encodes the @ncycles value as described in the datasheet
|
||||
* (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
|
||||
* helper which called with different parameter depending on the encoding
|
||||
* scheme.
|
||||
*
|
||||
* If the @ncycles value is too big to be encoded, -ERANGE is returned and
|
||||
* the encodedval is contains the maximum val. Otherwise, 0 is returned.
|
||||
*/
|
||||
static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
|
||||
unsigned int msbpos,
|
||||
unsigned int msbwidth,
|
||||
unsigned int msbfactor,
|
||||
unsigned int *encodedval)
|
||||
{
|
||||
unsigned int lsbmask = GENMASK(msbpos - 1, 0);
|
||||
unsigned int msbmask = GENMASK(msbwidth - 1, 0);
|
||||
unsigned int msb, lsb;
|
||||
int ret = 0;
|
||||
|
||||
msb = ncycles / msbfactor;
|
||||
lsb = ncycles % msbfactor;
|
||||
|
||||
if (lsb > lsbmask) {
|
||||
lsb = 0;
|
||||
msb++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's just put the maximum we can if the requested setting does
|
||||
* not fit in the register field.
|
||||
* We still return -ERANGE in case the caller cares.
|
||||
*/
|
||||
if (msb > msbmask) {
|
||||
msb = msbmask;
|
||||
lsb = lsbmask;
|
||||
ret = -ERANGE;
|
||||
}
|
||||
|
||||
*encodedval = (msb << msbpos) | lsb;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
|
||||
* specific value
|
||||
* @conf: SMC CS conf descriptor
|
||||
* @shift: the position of the Txx field in the TIMINGS register
|
||||
* @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
|
||||
* parameter
|
||||
*
|
||||
* This function encodes the @ncycles value as described in the datasheet
|
||||
* (section "SMC Timings Register"), and then stores the result in the
|
||||
* @conf->timings field at @shift position.
|
||||
*
|
||||
* Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
|
||||
* the field, and 0 otherwise.
|
||||
*/
|
||||
int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
|
||||
shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
|
||||
shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
|
||||
shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
|
||||
shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The formula described in atmel datasheets (section "HSMC Timings
|
||||
* Register"):
|
||||
*
|
||||
* ncycles = (Txx[3] * 64) + Txx[2:0]
|
||||
*/
|
||||
ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
|
||||
conf->timings &= ~GENMASK(shift + 3, shift);
|
||||
conf->timings |= val << shift;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
|
||||
* specific value
|
||||
* @conf: SMC CS conf descriptor
|
||||
* @shift: the position of the xx_SETUP field in the SETUP register
|
||||
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
|
||||
* parameter
|
||||
*
|
||||
* This function encodes the @ncycles value as described in the datasheet
|
||||
* (section "SMC Setup Register"), and then stores the result in the
|
||||
* @conf->setup field at @shift position.
|
||||
*
|
||||
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
|
||||
* the field, and 0 otherwise.
|
||||
*/
|
||||
int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
|
||||
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The formula described in atmel datasheets (section "SMC Setup
|
||||
* Register"):
|
||||
*
|
||||
* ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
|
||||
*/
|
||||
ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
|
||||
conf->setup &= ~GENMASK(shift + 7, shift);
|
||||
conf->setup |= val << shift;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
|
||||
* specific value
|
||||
* @conf: SMC CS conf descriptor
|
||||
* @shift: the position of the xx_PULSE field in the PULSE register
|
||||
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
|
||||
* parameter
|
||||
*
|
||||
* This function encodes the @ncycles value as described in the datasheet
|
||||
* (section "SMC Pulse Register"), and then stores the result in the
|
||||
* @conf->setup field at @shift position.
|
||||
*
|
||||
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
|
||||
* the field, and 0 otherwise.
|
||||
*/
|
||||
int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
|
||||
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The formula described in atmel datasheets (section "SMC Pulse
|
||||
* Register"):
|
||||
*
|
||||
* ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
|
||||
*/
|
||||
ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
|
||||
conf->pulse &= ~GENMASK(shift + 7, shift);
|
||||
conf->pulse |= val << shift;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
|
||||
* specific value
|
||||
* @conf: SMC CS conf descriptor
|
||||
* @shift: the position of the xx_CYCLE field in the CYCLE register
|
||||
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
|
||||
* parameter
|
||||
*
|
||||
* This function encodes the @ncycles value as described in the datasheet
|
||||
* (section "SMC Pulse Register"), and then stores the result in the
|
||||
* @conf->setup field at @shift position.
|
||||
*
|
||||
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
|
||||
* the field, and 0 otherwise.
|
||||
*/
|
||||
int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The formula described in atmel datasheets (section "SMC Cycle
|
||||
* Register"):
|
||||
*
|
||||
* ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
|
||||
*/
|
||||
ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
|
||||
conf->cycle &= ~GENMASK(shift + 15, shift);
|
||||
conf->cycle |= val << shift;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_apply - apply an SMC CS conf
|
||||
* @regmap: the SMC regmap
|
||||
* @cs: the CS id
|
||||
* @conf the SMC CS conf to apply
|
||||
*
|
||||
* Applies an SMC CS configuration.
|
||||
* Only valid on at91sam9/avr32 SoCs.
|
||||
*/
|
||||
void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
|
||||
const struct atmel_smc_cs_conf *conf)
|
||||
{
|
||||
regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
|
||||
regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
|
||||
regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
|
||||
regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
|
||||
|
||||
/**
|
||||
* atmel_hsmc_cs_conf_apply - apply an SMC CS conf
|
||||
* @regmap: the HSMC regmap
|
||||
* @cs: the CS id
|
||||
* @conf the SMC CS conf to apply
|
||||
*
|
||||
* Applies an SMC CS configuration.
|
||||
* Only valid on post-sama5 SoCs.
|
||||
*/
|
||||
void atmel_hsmc_cs_conf_apply(struct regmap *regmap, int cs,
|
||||
const struct atmel_smc_cs_conf *conf)
|
||||
{
|
||||
regmap_write(regmap, ATMEL_HSMC_SETUP(cs), conf->setup);
|
||||
regmap_write(regmap, ATMEL_HSMC_PULSE(cs), conf->pulse);
|
||||
regmap_write(regmap, ATMEL_HSMC_CYCLE(cs), conf->cycle);
|
||||
regmap_write(regmap, ATMEL_HSMC_TIMINGS(cs), conf->timings);
|
||||
regmap_write(regmap, ATMEL_HSMC_MODE(cs), conf->mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_get - retrieve the current SMC CS conf
|
||||
* @regmap: the SMC regmap
|
||||
* @cs: the CS id
|
||||
* @conf: the SMC CS conf object to store the current conf
|
||||
*
|
||||
* Retrieve the SMC CS configuration.
|
||||
* Only valid on at91sam9/avr32 SoCs.
|
||||
*/
|
||||
void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
|
||||
struct atmel_smc_cs_conf *conf)
|
||||
{
|
||||
regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
|
||||
regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
|
||||
regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
|
||||
regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
|
||||
|
||||
/**
|
||||
* atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
|
||||
* @regmap: the HSMC regmap
|
||||
* @cs: the CS id
|
||||
* @conf: the SMC CS conf object to store the current conf
|
||||
*
|
||||
* Retrieve the SMC CS configuration.
|
||||
* Only valid on post-sama5 SoCs.
|
||||
*/
|
||||
void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs,
|
||||
struct atmel_smc_cs_conf *conf)
|
||||
{
|
||||
regmap_read(regmap, ATMEL_HSMC_SETUP(cs), &conf->setup);
|
||||
regmap_read(regmap, ATMEL_HSMC_PULSE(cs), &conf->pulse);
|
||||
regmap_read(regmap, ATMEL_HSMC_CYCLE(cs), &conf->cycle);
|
||||
regmap_read(regmap, ATMEL_HSMC_TIMINGS(cs), &conf->timings);
|
||||
regmap_read(regmap, ATMEL_HSMC_MODE(cs), &conf->mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
|
@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
|
||||
|
||||
static const struct of_device_id axp20x_rsb_of_match[] = {
|
||||
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
|
||||
{ .compatible = "x-powers,axp803", .data = (void *)AXP803_ID },
|
||||
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
|
||||
{ .compatible = "x-powers,axp809", .data = (void *)AXP809_ID },
|
||||
{ },
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#define AXP20X_OFF 0x80
|
||||
|
||||
#define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0
|
||||
#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
|
||||
|
||||
static const char * const axp20x_model_names[] = {
|
||||
@ -40,6 +41,7 @@ static const char * const axp20x_model_names[] = {
|
||||
"AXP221",
|
||||
"AXP223",
|
||||
"AXP288",
|
||||
"AXP803",
|
||||
"AXP806",
|
||||
"AXP809",
|
||||
};
|
||||
@ -67,6 +69,7 @@ static const struct regmap_access_table axp152_volatile_table = {
|
||||
|
||||
static const struct regmap_range axp20x_writeable_ranges[] = {
|
||||
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
|
||||
regmap_reg_range(AXP20X_CHRG_CTRL1, AXP20X_CHRG_CTRL2),
|
||||
regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
|
||||
regmap_reg_range(AXP20X_RDC_H, AXP20X_OCV(AXP20X_OCV_MAX)),
|
||||
};
|
||||
@ -93,6 +96,7 @@ static const struct regmap_access_table axp20x_volatile_table = {
|
||||
/* AXP22x ranges are shared with the AXP809, as they cover the same range */
|
||||
static const struct regmap_range axp22x_writeable_ranges[] = {
|
||||
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
|
||||
regmap_reg_range(AXP20X_CHRG_CTRL1, AXP22X_CHRG_CTRL3),
|
||||
regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
|
||||
};
|
||||
|
||||
@ -100,7 +104,7 @@ static const struct regmap_range axp22x_volatile_ranges[] = {
|
||||
regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP20X_PWR_OP_MODE),
|
||||
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
|
||||
regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE),
|
||||
regmap_reg_range(AXP22X_PMIC_ADC_H, AXP20X_IPSOUT_V_HIGH_L),
|
||||
regmap_reg_range(AXP22X_PMIC_TEMP_H, AXP20X_IPSOUT_V_HIGH_L),
|
||||
regmap_reg_range(AXP20X_FG_RES, AXP20X_FG_RES),
|
||||
};
|
||||
|
||||
@ -114,6 +118,7 @@ static const struct regmap_access_table axp22x_volatile_table = {
|
||||
.n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges),
|
||||
};
|
||||
|
||||
/* AXP288 ranges are shared with the AXP803, as they cover the same range */
|
||||
static const struct regmap_range axp288_writeable_ranges[] = {
|
||||
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
|
||||
regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
|
||||
@ -261,6 +266,20 @@ static struct resource axp288_fuel_gauge_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource axp803_pek_resources[] = {
|
||||
{
|
||||
.name = "PEK_DBR",
|
||||
.start = AXP803_IRQ_PEK_RIS_EDGE,
|
||||
.end = AXP803_IRQ_PEK_RIS_EDGE,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}, {
|
||||
.name = "PEK_DBF",
|
||||
.start = AXP803_IRQ_PEK_FAL_EDGE,
|
||||
.end = AXP803_IRQ_PEK_FAL_EDGE,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource axp809_pek_resources[] = {
|
||||
{
|
||||
.name = "PEK_DBR",
|
||||
@ -454,6 +473,43 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1),
|
||||
};
|
||||
|
||||
static const struct regmap_irq axp803_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP803, ACIN_OVER_V, 0, 7),
|
||||
INIT_REGMAP_IRQ(AXP803, ACIN_PLUGIN, 0, 6),
|
||||
INIT_REGMAP_IRQ(AXP803, ACIN_REMOVAL, 0, 5),
|
||||
INIT_REGMAP_IRQ(AXP803, VBUS_OVER_V, 0, 4),
|
||||
INIT_REGMAP_IRQ(AXP803, VBUS_PLUGIN, 0, 3),
|
||||
INIT_REGMAP_IRQ(AXP803, VBUS_REMOVAL, 0, 2),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_PLUGIN, 1, 7),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_REMOVAL, 1, 6),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_ENT_ACT_MODE, 1, 5),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_EXIT_ACT_MODE, 1, 4),
|
||||
INIT_REGMAP_IRQ(AXP803, CHARG, 1, 3),
|
||||
INIT_REGMAP_IRQ(AXP803, CHARG_DONE, 1, 2),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_HIGH, 2, 7),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_HIGH_END, 2, 6),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_LOW, 2, 5),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_LOW_END, 2, 4),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_HIGH, 2, 3),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_HIGH_END, 2, 2),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_LOW, 2, 1),
|
||||
INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_LOW_END, 2, 0),
|
||||
INIT_REGMAP_IRQ(AXP803, DIE_TEMP_HIGH, 3, 7),
|
||||
INIT_REGMAP_IRQ(AXP803, GPADC, 3, 2),
|
||||
INIT_REGMAP_IRQ(AXP803, LOW_PWR_LVL1, 3, 1),
|
||||
INIT_REGMAP_IRQ(AXP803, LOW_PWR_LVL2, 3, 0),
|
||||
INIT_REGMAP_IRQ(AXP803, TIMER, 4, 7),
|
||||
INIT_REGMAP_IRQ(AXP803, PEK_RIS_EDGE, 4, 6),
|
||||
INIT_REGMAP_IRQ(AXP803, PEK_FAL_EDGE, 4, 5),
|
||||
INIT_REGMAP_IRQ(AXP803, PEK_SHORT, 4, 4),
|
||||
INIT_REGMAP_IRQ(AXP803, PEK_LONG, 4, 3),
|
||||
INIT_REGMAP_IRQ(AXP803, PEK_OVER_OFF, 4, 2),
|
||||
INIT_REGMAP_IRQ(AXP803, GPIO1_INPUT, 4, 1),
|
||||
INIT_REGMAP_IRQ(AXP803, GPIO0_INPUT, 4, 0),
|
||||
INIT_REGMAP_IRQ(AXP803, BC_USB_CHNG, 5, 1),
|
||||
INIT_REGMAP_IRQ(AXP803, MV_CHNG, 5, 0),
|
||||
};
|
||||
|
||||
static const struct regmap_irq axp806_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0),
|
||||
INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1),
|
||||
@ -554,6 +610,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = {
|
||||
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp803_regmap_irq_chip = {
|
||||
.name = "axp803",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
.ack_base = AXP20X_IRQ1_STATE,
|
||||
.mask_base = AXP20X_IRQ1_EN,
|
||||
.mask_invert = true,
|
||||
.init_ack_masked = true,
|
||||
.irqs = axp803_regmap_irqs,
|
||||
.num_irqs = ARRAY_SIZE(axp803_regmap_irqs),
|
||||
.num_regs = 6,
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp806_regmap_irq_chip = {
|
||||
.name = "axp806",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
@ -588,6 +656,11 @@ static struct mfd_cell axp20x_cells[] = {
|
||||
.resources = axp20x_pek_resources,
|
||||
}, {
|
||||
.name = "axp20x-regulator",
|
||||
}, {
|
||||
.name = "axp20x-adc",
|
||||
}, {
|
||||
.name = "axp20x-battery-power-supply",
|
||||
.of_compatible = "x-powers,axp209-battery-power-supply",
|
||||
}, {
|
||||
.name = "axp20x-ac-power-supply",
|
||||
.of_compatible = "x-powers,axp202-ac-power-supply",
|
||||
@ -608,6 +681,16 @@ static struct mfd_cell axp221_cells[] = {
|
||||
.resources = axp22x_pek_resources,
|
||||
}, {
|
||||
.name = "axp20x-regulator",
|
||||
}, {
|
||||
.name = "axp22x-adc"
|
||||
}, {
|
||||
.name = "axp20x-ac-power-supply",
|
||||
.of_compatible = "x-powers,axp221-ac-power-supply",
|
||||
.num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources),
|
||||
.resources = axp20x_ac_power_supply_resources,
|
||||
}, {
|
||||
.name = "axp20x-battery-power-supply",
|
||||
.of_compatible = "x-powers,axp221-battery-power-supply",
|
||||
}, {
|
||||
.name = "axp20x-usb-power-supply",
|
||||
.of_compatible = "x-powers,axp221-usb-power-supply",
|
||||
@ -621,8 +704,18 @@ static struct mfd_cell axp223_cells[] = {
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
|
||||
.resources = axp22x_pek_resources,
|
||||
}, {
|
||||
.name = "axp22x-adc",
|
||||
}, {
|
||||
.name = "axp20x-battery-power-supply",
|
||||
.of_compatible = "x-powers,axp221-battery-power-supply",
|
||||
}, {
|
||||
.name = "axp20x-regulator",
|
||||
}, {
|
||||
.name = "axp20x-ac-power-supply",
|
||||
.of_compatible = "x-powers,axp221-ac-power-supply",
|
||||
.num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources),
|
||||
.resources = axp20x_ac_power_supply_resources,
|
||||
}, {
|
||||
.name = "axp20x-usb-power-supply",
|
||||
.of_compatible = "x-powers,axp223-usb-power-supply",
|
||||
@ -750,6 +843,14 @@ static struct mfd_cell axp288_cells[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp803_cells[] = {
|
||||
{
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp803_pek_resources),
|
||||
.resources = axp803_pek_resources,
|
||||
}
|
||||
};
|
||||
|
||||
static struct mfd_cell axp806_cells[] = {
|
||||
{
|
||||
.id = 2,
|
||||
@ -836,6 +937,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
|
||||
axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
|
||||
axp20x->irq_flags = IRQF_TRIGGER_LOW;
|
||||
break;
|
||||
case AXP803_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp803_cells);
|
||||
axp20x->cells = axp803_cells;
|
||||
axp20x->regmap_cfg = &axp288_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp803_regmap_irq_chip;
|
||||
break;
|
||||
case AXP806_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
|
||||
axp20x->cells = axp806_cells;
|
||||
@ -877,15 +984,19 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
|
||||
* the these device addressing bits (in the upper 4 bits of the
|
||||
* registers) match.
|
||||
*
|
||||
* Since we only support an AXP806 chained to an AXP809 in slave
|
||||
* mode, and there isn't any existing hardware which uses AXP806
|
||||
* in master mode, or has 2 AXP806s in the same system, we can
|
||||
* just program the register address extension to the slave mode
|
||||
* address.
|
||||
* By default we support an AXP806 chained to an AXP809 in slave
|
||||
* mode. Boards which use an AXP806 in master mode can set the
|
||||
* property "x-powers,master-mode" to override the default.
|
||||
*/
|
||||
if (axp20x->variant == AXP806_ID)
|
||||
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
|
||||
AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
|
||||
if (axp20x->variant == AXP806_ID) {
|
||||
if (of_property_read_bool(axp20x->dev->of_node,
|
||||
"x-powers,master-mode"))
|
||||
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
|
||||
AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE);
|
||||
else
|
||||
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
|
||||
AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags,
|
||||
|
@ -166,6 +166,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
|
||||
dev_info(dev, "Chrome EC device registered\n");
|
||||
|
||||
cros_ec_acpi_install_gpe_handler(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_mfd:
|
||||
@ -179,6 +181,8 @@ int cros_ec_remove(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
mfd_remove_devices(ec_dev->dev);
|
||||
|
||||
cros_ec_acpi_remove_gpe_handler();
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_remove);
|
||||
@ -190,9 +194,14 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev)
|
||||
int ret;
|
||||
u8 sleep_event;
|
||||
|
||||
sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
|
||||
HOST_SLEEP_EVENT_S3_RESUME :
|
||||
HOST_SLEEP_EVENT_S0IX_RESUME;
|
||||
if (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) {
|
||||
sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND;
|
||||
} else {
|
||||
sleep_event = HOST_SLEEP_EVENT_S0IX_SUSPEND;
|
||||
|
||||
/* Clearing the GPE status for any pending event */
|
||||
cros_ec_acpi_clear_gpe();
|
||||
}
|
||||
|
||||
ret = cros_ec_sleep_event(ec_dev, sleep_event);
|
||||
if (ret < 0)
|
||||
|
103
drivers/mfd/cros_ec_acpi_gpe.c
Normal file
103
drivers/mfd/cros_ec_acpi_gpe.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* ChromeOS EC multi-function device
|
||||
*
|
||||
* Copyright (C) 2017 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The ChromeOS EC multi function device is used to mux all the requests
|
||||
* to the EC device for its multiple features: keyboard controller,
|
||||
* battery charging and regulator control, firmware update.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define ACPI_LID_DEVICE "LID0"
|
||||
|
||||
static int ec_wake_gpe = -EINVAL;
|
||||
|
||||
/*
|
||||
* This handler indicates to ACPI core that this GPE should stay enabled for
|
||||
* lid to work in suspend to idle path.
|
||||
*/
|
||||
static u32 cros_ec_gpe_handler(acpi_handle gpe_device, u32 gpe_number,
|
||||
void *data)
|
||||
{
|
||||
return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get ACPI GPE for LID0 device.
|
||||
*/
|
||||
static int cros_ec_get_ec_wake_gpe(struct device *dev)
|
||||
{
|
||||
struct acpi_device *cros_acpi_dev;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
int ret;
|
||||
|
||||
cros_acpi_dev = ACPI_COMPANION(dev);
|
||||
|
||||
if (!cros_acpi_dev || !cros_acpi_dev->parent ||
|
||||
!cros_acpi_dev->parent->handle)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_get_handle(cros_acpi_dev->parent->handle, ACPI_LID_DEVICE,
|
||||
&handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EINVAL;
|
||||
|
||||
ret = acpi_bus_get_device(handle, &adev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adev->wakeup.gpe_number;
|
||||
}
|
||||
|
||||
int cros_ec_acpi_install_gpe_handler(struct device *dev)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
ec_wake_gpe = cros_ec_get_ec_wake_gpe(dev);
|
||||
|
||||
if (ec_wake_gpe < 0)
|
||||
return ec_wake_gpe;
|
||||
|
||||
status = acpi_install_gpe_handler(NULL, ec_wake_gpe,
|
||||
ACPI_GPE_EDGE_TRIGGERED,
|
||||
&cros_ec_gpe_handler, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(dev, "Initialized, GPE = 0x%x\n", ec_wake_gpe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cros_ec_acpi_remove_gpe_handler(void)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
if (ec_wake_gpe < 0)
|
||||
return;
|
||||
|
||||
status = acpi_remove_gpe_handler(NULL, ec_wake_gpe,
|
||||
&cros_ec_gpe_handler);
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_err("failed to remove gpe handler\n");
|
||||
}
|
||||
|
||||
void cros_ec_acpi_clear_gpe(void)
|
||||
{
|
||||
if (ec_wake_gpe < 0)
|
||||
return;
|
||||
|
||||
acpi_clear_gpe(NULL, ec_wake_gpe);
|
||||
}
|
@ -45,8 +45,11 @@
|
||||
* on the other end and need to transfer ~256 bytes, then we need:
|
||||
* 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms
|
||||
*
|
||||
* We'll wait 4 times that to handle clock stretching and other
|
||||
* paranoia.
|
||||
* We'll wait 8 times that to handle clock stretching and other
|
||||
* paranoia. Note that some battery gas gauge ICs claim to have a
|
||||
* clock stretch of 144ms in rare situations. That's incentive for
|
||||
* not directly passing i2c through, but it's too late for that for
|
||||
* existing hardware.
|
||||
*
|
||||
* It's pretty unlikely that we'll really see a 249 byte tunnel in
|
||||
* anything other than testing. If this was more common we might
|
||||
@ -54,7 +57,7 @@
|
||||
* wait loop. The 'flash write' command would be another candidate
|
||||
* for this, clocking in at 2-3ms.
|
||||
*/
|
||||
#define EC_MSG_DEADLINE_MS 100
|
||||
#define EC_MSG_DEADLINE_MS 200
|
||||
|
||||
/*
|
||||
* Time between raising the SPI chip select (for the end of a
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Core, IRQ and I2C device driver for DA9062 PMIC
|
||||
* Copyright (C) 2015 Dialog Semiconductor Ltd.
|
||||
* Core, IRQ and I2C device driver for DA9061 and DA9062 PMICs
|
||||
* Copyright (C) 2015-2017 Dialog Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -30,6 +30,70 @@
|
||||
#define DA9062_REG_EVENT_B_OFFSET 1
|
||||
#define DA9062_REG_EVENT_C_OFFSET 2
|
||||
|
||||
static struct regmap_irq da9061_irqs[] = {
|
||||
/* EVENT A */
|
||||
[DA9061_IRQ_ONKEY] = {
|
||||
.reg_offset = DA9062_REG_EVENT_A_OFFSET,
|
||||
.mask = DA9062AA_M_NONKEY_MASK,
|
||||
},
|
||||
[DA9061_IRQ_WDG_WARN] = {
|
||||
.reg_offset = DA9062_REG_EVENT_A_OFFSET,
|
||||
.mask = DA9062AA_M_WDG_WARN_MASK,
|
||||
},
|
||||
[DA9061_IRQ_SEQ_RDY] = {
|
||||
.reg_offset = DA9062_REG_EVENT_A_OFFSET,
|
||||
.mask = DA9062AA_M_SEQ_RDY_MASK,
|
||||
},
|
||||
/* EVENT B */
|
||||
[DA9061_IRQ_TEMP] = {
|
||||
.reg_offset = DA9062_REG_EVENT_B_OFFSET,
|
||||
.mask = DA9062AA_M_TEMP_MASK,
|
||||
},
|
||||
[DA9061_IRQ_LDO_LIM] = {
|
||||
.reg_offset = DA9062_REG_EVENT_B_OFFSET,
|
||||
.mask = DA9062AA_M_LDO_LIM_MASK,
|
||||
},
|
||||
[DA9061_IRQ_DVC_RDY] = {
|
||||
.reg_offset = DA9062_REG_EVENT_B_OFFSET,
|
||||
.mask = DA9062AA_M_DVC_RDY_MASK,
|
||||
},
|
||||
[DA9061_IRQ_VDD_WARN] = {
|
||||
.reg_offset = DA9062_REG_EVENT_B_OFFSET,
|
||||
.mask = DA9062AA_M_VDD_WARN_MASK,
|
||||
},
|
||||
/* EVENT C */
|
||||
[DA9061_IRQ_GPI0] = {
|
||||
.reg_offset = DA9062_REG_EVENT_C_OFFSET,
|
||||
.mask = DA9062AA_M_GPI0_MASK,
|
||||
},
|
||||
[DA9061_IRQ_GPI1] = {
|
||||
.reg_offset = DA9062_REG_EVENT_C_OFFSET,
|
||||
.mask = DA9062AA_M_GPI1_MASK,
|
||||
},
|
||||
[DA9061_IRQ_GPI2] = {
|
||||
.reg_offset = DA9062_REG_EVENT_C_OFFSET,
|
||||
.mask = DA9062AA_M_GPI2_MASK,
|
||||
},
|
||||
[DA9061_IRQ_GPI3] = {
|
||||
.reg_offset = DA9062_REG_EVENT_C_OFFSET,
|
||||
.mask = DA9062AA_M_GPI3_MASK,
|
||||
},
|
||||
[DA9061_IRQ_GPI4] = {
|
||||
.reg_offset = DA9062_REG_EVENT_C_OFFSET,
|
||||
.mask = DA9062AA_M_GPI4_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip da9061_irq_chip = {
|
||||
.name = "da9061-irq",
|
||||
.irqs = da9061_irqs,
|
||||
.num_irqs = DA9061_NUM_IRQ,
|
||||
.num_regs = 3,
|
||||
.status_base = DA9062AA_EVENT_A,
|
||||
.mask_base = DA9062AA_IRQ_MASK_A,
|
||||
.ack_base = DA9062AA_EVENT_A,
|
||||
};
|
||||
|
||||
static struct regmap_irq da9062_irqs[] = {
|
||||
/* EVENT A */
|
||||
[DA9062_IRQ_ONKEY] = {
|
||||
@ -102,6 +166,57 @@ static struct regmap_irq_chip da9062_irq_chip = {
|
||||
.ack_base = DA9062AA_EVENT_A,
|
||||
};
|
||||
|
||||
static struct resource da9061_core_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(DA9061_IRQ_VDD_WARN, "VDD_WARN"),
|
||||
};
|
||||
|
||||
static struct resource da9061_regulators_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(DA9061_IRQ_LDO_LIM, "LDO_LIM"),
|
||||
};
|
||||
|
||||
static struct resource da9061_thermal_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(DA9061_IRQ_TEMP, "THERMAL"),
|
||||
};
|
||||
|
||||
static struct resource da9061_wdt_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(DA9061_IRQ_WDG_WARN, "WD_WARN"),
|
||||
};
|
||||
|
||||
static struct resource da9061_onkey_resources[] = {
|
||||
DEFINE_RES_IRQ_NAMED(DA9061_IRQ_ONKEY, "ONKEY"),
|
||||
};
|
||||
|
||||
static const struct mfd_cell da9061_devs[] = {
|
||||
{
|
||||
.name = "da9061-core",
|
||||
.num_resources = ARRAY_SIZE(da9061_core_resources),
|
||||
.resources = da9061_core_resources,
|
||||
},
|
||||
{
|
||||
.name = "da9062-regulators",
|
||||
.num_resources = ARRAY_SIZE(da9061_regulators_resources),
|
||||
.resources = da9061_regulators_resources,
|
||||
},
|
||||
{
|
||||
.name = "da9061-watchdog",
|
||||
.num_resources = ARRAY_SIZE(da9061_wdt_resources),
|
||||
.resources = da9061_wdt_resources,
|
||||
.of_compatible = "dlg,da9061-watchdog",
|
||||
},
|
||||
{
|
||||
.name = "da9061-thermal",
|
||||
.num_resources = ARRAY_SIZE(da9061_thermal_resources),
|
||||
.resources = da9061_thermal_resources,
|
||||
.of_compatible = "dlg,da9061-thermal",
|
||||
},
|
||||
{
|
||||
.name = "da9061-onkey",
|
||||
.num_resources = ARRAY_SIZE(da9061_onkey_resources),
|
||||
.resources = da9061_onkey_resources,
|
||||
.of_compatible = "dlg,da9061-onkey",
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource da9062_core_resources[] = {
|
||||
DEFINE_RES_NAMED(DA9062_IRQ_VDD_WARN, 1, "VDD_WARN", IORESOURCE_IRQ),
|
||||
};
|
||||
@ -200,7 +315,8 @@ static int da9062_clear_fault_log(struct da9062 *chip)
|
||||
|
||||
static int da9062_get_device_type(struct da9062 *chip)
|
||||
{
|
||||
int device_id, variant_id, variant_mrc;
|
||||
int device_id, variant_id, variant_mrc, variant_vrc;
|
||||
char *type;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chip->regmap, DA9062AA_DEVICE_ID, &device_id);
|
||||
@ -219,9 +335,23 @@ static int da9062_get_device_type(struct da9062 *chip)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
variant_vrc = (variant_id & DA9062AA_VRC_MASK) >> DA9062AA_VRC_SHIFT;
|
||||
|
||||
switch (variant_vrc) {
|
||||
case DA9062_PMIC_VARIANT_VRC_DA9061:
|
||||
type = "DA9061";
|
||||
break;
|
||||
case DA9062_PMIC_VARIANT_VRC_DA9062:
|
||||
type = "DA9062";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(chip->dev,
|
||||
"Device detected (device-ID: 0x%02X, var-ID: 0x%02X)\n",
|
||||
device_id, variant_id);
|
||||
"Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n",
|
||||
device_id, variant_id, type);
|
||||
|
||||
variant_mrc = (variant_id & DA9062AA_MRC_MASK) >> DA9062AA_MRC_SHIFT;
|
||||
|
||||
@ -234,6 +364,234 @@ static int da9062_get_device_type(struct da9062 *chip)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct regmap_range da9061_aa_readable_ranges[] = {
|
||||
{
|
||||
.range_min = DA9062AA_PAGE_CON,
|
||||
.range_max = DA9062AA_STATUS_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_STATUS_D,
|
||||
.range_max = DA9062AA_EVENT_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_IRQ_MASK_A,
|
||||
.range_max = DA9062AA_IRQ_MASK_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONTROL_A,
|
||||
.range_max = DA9062AA_GPIO_4,
|
||||
}, {
|
||||
.range_min = DA9062AA_GPIO_WKUP_MODE,
|
||||
.range_max = DA9062AA_GPIO_OUT3_4,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK1_CONT,
|
||||
.range_max = DA9062AA_BUCK4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK3_CONT,
|
||||
.range_max = DA9062AA_BUCK3_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_LDO1_CONT,
|
||||
.range_max = DA9062AA_LDO4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_DVC_1,
|
||||
.range_max = DA9062AA_DVC_1,
|
||||
}, {
|
||||
.range_min = DA9062AA_SEQ,
|
||||
.range_max = DA9062AA_ID_4_3,
|
||||
}, {
|
||||
.range_min = DA9062AA_ID_12_11,
|
||||
.range_max = DA9062AA_ID_16_15,
|
||||
}, {
|
||||
.range_min = DA9062AA_ID_22_21,
|
||||
.range_max = DA9062AA_ID_32_31,
|
||||
}, {
|
||||
.range_min = DA9062AA_SEQ_A,
|
||||
.range_max = DA9062AA_WAIT,
|
||||
}, {
|
||||
.range_min = DA9062AA_RESET,
|
||||
.range_max = DA9062AA_BUCK_ILIM_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK1_CFG,
|
||||
.range_max = DA9062AA_BUCK3_CFG,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK1_A,
|
||||
.range_max = DA9062AA_VBUCK4_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK3_A,
|
||||
.range_max = DA9062AA_VBUCK3_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VLDO1_A,
|
||||
.range_max = DA9062AA_VLDO4_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK1_B,
|
||||
.range_max = DA9062AA_VBUCK4_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK3_B,
|
||||
.range_max = DA9062AA_VBUCK3_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_VLDO1_B,
|
||||
.range_max = DA9062AA_VLDO4_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_BBAT_CONT,
|
||||
.range_max = DA9062AA_BBAT_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_INTERFACE,
|
||||
.range_max = DA9062AA_CONFIG_E,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONFIG_G,
|
||||
.range_max = DA9062AA_CONFIG_K,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONFIG_M,
|
||||
.range_max = DA9062AA_CONFIG_M,
|
||||
}, {
|
||||
.range_min = DA9062AA_GP_ID_0,
|
||||
.range_max = DA9062AA_GP_ID_19,
|
||||
}, {
|
||||
.range_min = DA9062AA_DEVICE_ID,
|
||||
.range_max = DA9062AA_CONFIG_ID,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range da9061_aa_writeable_ranges[] = {
|
||||
{
|
||||
.range_min = DA9062AA_PAGE_CON,
|
||||
.range_max = DA9062AA_PAGE_CON,
|
||||
}, {
|
||||
.range_min = DA9062AA_FAULT_LOG,
|
||||
.range_max = DA9062AA_EVENT_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_IRQ_MASK_A,
|
||||
.range_max = DA9062AA_IRQ_MASK_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONTROL_A,
|
||||
.range_max = DA9062AA_GPIO_4,
|
||||
}, {
|
||||
.range_min = DA9062AA_GPIO_WKUP_MODE,
|
||||
.range_max = DA9062AA_GPIO_OUT3_4,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK1_CONT,
|
||||
.range_max = DA9062AA_BUCK4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK3_CONT,
|
||||
.range_max = DA9062AA_BUCK3_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_LDO1_CONT,
|
||||
.range_max = DA9062AA_LDO4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_DVC_1,
|
||||
.range_max = DA9062AA_DVC_1,
|
||||
}, {
|
||||
.range_min = DA9062AA_SEQ,
|
||||
.range_max = DA9062AA_ID_4_3,
|
||||
}, {
|
||||
.range_min = DA9062AA_ID_12_11,
|
||||
.range_max = DA9062AA_ID_16_15,
|
||||
}, {
|
||||
.range_min = DA9062AA_ID_22_21,
|
||||
.range_max = DA9062AA_ID_32_31,
|
||||
}, {
|
||||
.range_min = DA9062AA_SEQ_A,
|
||||
.range_max = DA9062AA_WAIT,
|
||||
}, {
|
||||
.range_min = DA9062AA_RESET,
|
||||
.range_max = DA9062AA_BUCK_ILIM_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK1_CFG,
|
||||
.range_max = DA9062AA_BUCK3_CFG,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK1_A,
|
||||
.range_max = DA9062AA_VBUCK4_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK3_A,
|
||||
.range_max = DA9062AA_VBUCK3_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VLDO1_A,
|
||||
.range_max = DA9062AA_VLDO4_A,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK1_B,
|
||||
.range_max = DA9062AA_VBUCK4_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_VBUCK3_B,
|
||||
.range_max = DA9062AA_VBUCK3_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_VLDO1_B,
|
||||
.range_max = DA9062AA_VLDO4_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_BBAT_CONT,
|
||||
.range_max = DA9062AA_BBAT_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_GP_ID_0,
|
||||
.range_max = DA9062AA_GP_ID_19,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range da9061_aa_volatile_ranges[] = {
|
||||
{
|
||||
.range_min = DA9062AA_PAGE_CON,
|
||||
.range_max = DA9062AA_STATUS_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_STATUS_D,
|
||||
.range_max = DA9062AA_EVENT_C,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONTROL_A,
|
||||
.range_max = DA9062AA_CONTROL_B,
|
||||
}, {
|
||||
.range_min = DA9062AA_CONTROL_E,
|
||||
.range_max = DA9062AA_CONTROL_F,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK1_CONT,
|
||||
.range_max = DA9062AA_BUCK4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_BUCK3_CONT,
|
||||
.range_max = DA9062AA_BUCK3_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_LDO1_CONT,
|
||||
.range_max = DA9062AA_LDO4_CONT,
|
||||
}, {
|
||||
.range_min = DA9062AA_DVC_1,
|
||||
.range_max = DA9062AA_DVC_1,
|
||||
}, {
|
||||
.range_min = DA9062AA_SEQ,
|
||||
.range_max = DA9062AA_SEQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_access_table da9061_aa_readable_table = {
|
||||
.yes_ranges = da9061_aa_readable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(da9061_aa_readable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table da9061_aa_writeable_table = {
|
||||
.yes_ranges = da9061_aa_writeable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(da9061_aa_writeable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table da9061_aa_volatile_table = {
|
||||
.yes_ranges = da9061_aa_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(da9061_aa_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range_cfg da9061_range_cfg[] = {
|
||||
{
|
||||
.range_min = DA9062AA_PAGE_CON,
|
||||
.range_max = DA9062AA_CONFIG_ID,
|
||||
.selector_reg = DA9062AA_PAGE_CON,
|
||||
.selector_mask = 1 << DA9062_I2C_PAGE_SEL_SHIFT,
|
||||
.selector_shift = DA9062_I2C_PAGE_SEL_SHIFT,
|
||||
.window_start = 0,
|
||||
.window_len = 256,
|
||||
}
|
||||
};
|
||||
|
||||
static struct regmap_config da9061_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.ranges = da9061_range_cfg,
|
||||
.num_ranges = ARRAY_SIZE(da9061_range_cfg),
|
||||
.max_register = DA9062AA_CONFIG_ID,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.rd_table = &da9061_aa_readable_table,
|
||||
.wr_table = &da9061_aa_writeable_table,
|
||||
.volatile_table = &da9061_aa_volatile_table,
|
||||
};
|
||||
|
||||
static const struct regmap_range da9062_aa_readable_ranges[] = {
|
||||
{
|
||||
.range_min = DA9062AA_PAGE_CON,
|
||||
@ -456,17 +814,39 @@ static struct regmap_config da9062_regmap_config = {
|
||||
.volatile_table = &da9062_aa_volatile_table,
|
||||
};
|
||||
|
||||
static const struct of_device_id da9062_dt_ids[] = {
|
||||
{ .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061, },
|
||||
{ .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, da9062_dt_ids);
|
||||
|
||||
static int da9062_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct da9062 *chip;
|
||||
const struct of_device_id *match;
|
||||
unsigned int irq_base;
|
||||
const struct mfd_cell *cell;
|
||||
const struct regmap_irq_chip *irq_chip;
|
||||
const struct regmap_config *config;
|
||||
int cell_num;
|
||||
int ret;
|
||||
|
||||
chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
match = of_match_node(da9062_dt_ids, i2c->dev.of_node);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
chip->chip_type = (uintptr_t)match->data;
|
||||
} else {
|
||||
chip->chip_type = id->driver_data;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, chip);
|
||||
chip->dev = &i2c->dev;
|
||||
|
||||
@ -475,7 +855,25 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(i2c, &da9062_regmap_config);
|
||||
switch (chip->chip_type) {
|
||||
case COMPAT_TYPE_DA9061:
|
||||
cell = da9061_devs;
|
||||
cell_num = ARRAY_SIZE(da9061_devs);
|
||||
irq_chip = &da9061_irq_chip;
|
||||
config = &da9061_regmap_config;
|
||||
break;
|
||||
case COMPAT_TYPE_DA9062:
|
||||
cell = da9062_devs;
|
||||
cell_num = ARRAY_SIZE(da9062_devs);
|
||||
irq_chip = &da9062_irq_chip;
|
||||
config = &da9062_regmap_config;
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->dev, "Unrecognised chip type\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(i2c, config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
ret = PTR_ERR(chip->regmap);
|
||||
dev_err(chip->dev, "Failed to allocate register map: %d\n",
|
||||
@ -493,7 +891,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
ret = regmap_add_irq_chip(chip->regmap, i2c->irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
|
||||
-1, &da9062_irq_chip,
|
||||
-1, irq_chip,
|
||||
&chip->regmap_irq);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "Failed to request IRQ %d: %d\n",
|
||||
@ -503,8 +901,8 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
irq_base = regmap_irq_chip_get_base(chip->regmap_irq);
|
||||
|
||||
ret = mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE, da9062_devs,
|
||||
ARRAY_SIZE(da9062_devs), NULL, irq_base,
|
||||
ret = mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE, cell,
|
||||
cell_num, NULL, irq_base,
|
||||
NULL);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "Cannot register child devices\n");
|
||||
@ -526,17 +924,12 @@ static int da9062_i2c_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id da9062_i2c_id[] = {
|
||||
{ "da9062", 0 },
|
||||
{ "da9061", COMPAT_TYPE_DA9061 },
|
||||
{ "da9062", COMPAT_TYPE_DA9062 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, da9062_i2c_id);
|
||||
|
||||
static const struct of_device_id da9062_dt_ids[] = {
|
||||
{ .compatible = "dlg,da9062", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, da9062_dt_ids);
|
||||
|
||||
static struct i2c_driver da9062_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "da9062",
|
||||
@ -549,6 +942,6 @@ static struct i2c_driver da9062_i2c_driver = {
|
||||
|
||||
module_i2c_driver(da9062_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Core device driver for Dialog DA9062");
|
||||
MODULE_DESCRIPTION("Core device driver for Dialog DA9061 and DA9062");
|
||||
MODULE_AUTHOR("Steve Twiss <stwiss.opensource@diasemi.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -2377,7 +2377,7 @@ static void ack_dbb_wakeup(void)
|
||||
|
||||
static inline void print_unknown_header_warning(u8 n, u8 header)
|
||||
{
|
||||
pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
|
||||
pr_warn("prcmu: Unknown message header (%d) in mailbox %d\n",
|
||||
header, n);
|
||||
}
|
||||
|
||||
|
@ -14,15 +14,17 @@
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/exynos5-pmu.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/soc/samsung/exynos-regs-pmu.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* LPASS Top register definitions */
|
||||
@ -51,10 +53,9 @@
|
||||
#define LPASS_INTR_SFR BIT(0)
|
||||
|
||||
struct exynos_lpass {
|
||||
/* pointer to the Power Management Unit regmap */
|
||||
struct regmap *pmu;
|
||||
/* pointer to the LPASS TOP regmap */
|
||||
struct regmap *top;
|
||||
struct clk *sfr0_clk;
|
||||
};
|
||||
|
||||
static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask)
|
||||
@ -74,6 +75,8 @@ static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask)
|
||||
|
||||
static void exynos_lpass_enable(struct exynos_lpass *lpass)
|
||||
{
|
||||
clk_prepare_enable(lpass->sfr0_clk);
|
||||
|
||||
/* Unmask SFR, DMA and I2S interrupt */
|
||||
regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK,
|
||||
LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S);
|
||||
@ -81,10 +84,6 @@ static void exynos_lpass_enable(struct exynos_lpass *lpass)
|
||||
regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK,
|
||||
LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S);
|
||||
|
||||
/* Activate related PADs from retention state */
|
||||
regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION,
|
||||
EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR);
|
||||
|
||||
exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET);
|
||||
exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET);
|
||||
exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET);
|
||||
@ -96,8 +95,7 @@ static void exynos_lpass_disable(struct exynos_lpass *lpass)
|
||||
regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0);
|
||||
regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0);
|
||||
|
||||
/* Deactivate related PADs from retention state */
|
||||
regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, 0);
|
||||
clk_disable_unprepare(lpass->sfr0_clk);
|
||||
}
|
||||
|
||||
static const struct regmap_config exynos_lpass_reg_conf = {
|
||||
@ -124,6 +122,10 @@ static int exynos_lpass_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base_top))
|
||||
return PTR_ERR(base_top);
|
||||
|
||||
lpass->sfr0_clk = devm_clk_get(dev, "sfr0_ctrl");
|
||||
if (IS_ERR(lpass->sfr0_clk))
|
||||
return PTR_ERR(lpass->sfr0_clk);
|
||||
|
||||
lpass->top = regmap_init_mmio(dev, base_top,
|
||||
&exynos_lpass_reg_conf);
|
||||
if (IS_ERR(lpass->top)) {
|
||||
@ -131,19 +133,27 @@ static int exynos_lpass_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(lpass->top);
|
||||
}
|
||||
|
||||
lpass->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,pmu-syscon");
|
||||
if (IS_ERR(lpass->pmu)) {
|
||||
dev_err(dev, "Failed to lookup PMU regmap\n");
|
||||
return PTR_ERR(lpass->pmu);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, lpass);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
exynos_lpass_enable(lpass);
|
||||
|
||||
return of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
static int exynos_lpass_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_lpass *lpass = platform_get_drvdata(pdev);
|
||||
|
||||
exynos_lpass_disable(lpass);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
exynos_lpass_disable(lpass);
|
||||
regmap_exit(lpass->top);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused exynos_lpass_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_lpass *lpass = dev_get_drvdata(dev);
|
||||
@ -162,8 +172,11 @@ static int __maybe_unused exynos_lpass_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(lpass_pm_ops, exynos_lpass_suspend,
|
||||
exynos_lpass_resume);
|
||||
static const struct dev_pm_ops lpass_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(exynos_lpass_suspend, exynos_lpass_resume, NULL)
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_lpass_of_match[] = {
|
||||
{ .compatible = "samsung,exynos5433-lpass" },
|
||||
@ -178,6 +191,7 @@ static struct platform_driver exynos_lpass_driver = {
|
||||
.of_match_table = exynos_lpass_of_match,
|
||||
},
|
||||
.probe = exynos_lpass_probe,
|
||||
.remove = exynos_lpass_remove,
|
||||
};
|
||||
module_platform_driver(exynos_lpass_driver);
|
||||
|
||||
|
@ -77,7 +77,8 @@ static const struct mfd_cell hi655x_pmic_devs[] = {
|
||||
.num_resources = ARRAY_SIZE(pwrkey_resources),
|
||||
.resources = &pwrkey_resources[0],
|
||||
},
|
||||
{ .name = "hi655x-regulator", },
|
||||
{ .name = "hi655x-regulator", },
|
||||
{ .name = "hi655x-clk", },
|
||||
};
|
||||
|
||||
static void hi655x_local_irq_clear(struct regmap *map)
|
||||
|
@ -22,10 +22,6 @@
|
||||
|
||||
#include "intel-lpss.h"
|
||||
|
||||
static const struct intel_lpss_platform_info spt_info = {
|
||||
.clk_rate = 120000000,
|
||||
};
|
||||
|
||||
static struct property_entry spt_i2c_properties[] = {
|
||||
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230),
|
||||
{ },
|
||||
|
@ -20,7 +20,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/intel_bxtwc.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/mfd/intel_soc_pmic_bxtwc.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
|
||||
/* PMIC device registers */
|
||||
@ -237,15 +238,14 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
u8 ipc_out[4];
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
|
||||
if (!pmic)
|
||||
return -EINVAL;
|
||||
|
||||
if (reg & REG_ADDR_MASK)
|
||||
i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
|
||||
else {
|
||||
else
|
||||
i2c_addr = BXTWC_DEVICE1_ADDR;
|
||||
if (!i2c_addr) {
|
||||
dev_err(pmic->dev, "I2C address not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
reg &= REG_OFFSET_MASK;
|
||||
|
||||
ipc_in[0] = reg;
|
||||
@ -270,15 +270,14 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
u8 ipc_in[3];
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
|
||||
if (!pmic)
|
||||
return -EINVAL;
|
||||
|
||||
if (reg & REG_ADDR_MASK)
|
||||
i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
|
||||
else {
|
||||
else
|
||||
i2c_addr = BXTWC_DEVICE1_ADDR;
|
||||
if (!i2c_addr) {
|
||||
dev_err(pmic->dev, "I2C address not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
reg &= REG_OFFSET_MASK;
|
||||
|
||||
ipc_in[0] = reg;
|
||||
|
@ -44,22 +44,6 @@ static struct pwm_lookup crc_pwm_lookup[] = {
|
||||
PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL),
|
||||
};
|
||||
|
||||
static int intel_soc_pmic_find_gpio_irq(struct device *dev)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
int irq;
|
||||
|
||||
desc = devm_gpiod_get_index(dev, "intel_soc_pmic", 0, GPIOD_IN);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
irq = gpiod_to_irq(desc);
|
||||
if (irq < 0)
|
||||
dev_warn(dev, "Can't get irq: %d\n", irq);
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *i2c_id)
|
||||
{
|
||||
@ -68,7 +52,6 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
|
||||
struct intel_soc_pmic_config *config;
|
||||
struct intel_soc_pmic *pmic;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id || !id->driver_data)
|
||||
@ -83,14 +66,10 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
|
||||
dev_set_drvdata(dev, pmic);
|
||||
|
||||
pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config);
|
||||
if (IS_ERR(pmic->regmap))
|
||||
return PTR_ERR(pmic->regmap);
|
||||
|
||||
/*
|
||||
* On some boards the PMIC interrupt may come from a GPIO line. Try to
|
||||
* lookup the ACPI table for a such connection and setup a GPIO
|
||||
* interrupt if it exists. Otherwise use the IRQ provided by I2C
|
||||
*/
|
||||
irq = intel_soc_pmic_find_gpio_irq(dev);
|
||||
pmic->irq = (irq < 0) ? i2c->irq : irq;
|
||||
pmic->irq = i2c->irq;
|
||||
|
||||
ret = regmap_add_irq_chip(pmic->regmap, pmic->irq,
|
||||
config->irq_flags | IRQF_ONESHOT,
|
||||
|
@ -400,9 +400,6 @@ static int __init micro_probe(struct platform_device *pdev)
|
||||
micro->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
micro->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(micro->base))
|
||||
return PTR_ERR(micro->base);
|
||||
|
@ -227,6 +227,8 @@ enum lpc_chipsets {
|
||||
LPC_LEWISBURG, /* Lewisburg */
|
||||
LPC_9S, /* 9 Series */
|
||||
LPC_APL, /* Apollo Lake SoC */
|
||||
LPC_GLK, /* Gemini Lake SoC */
|
||||
LPC_COUGARMOUNTAIN,/* Cougar Mountain SoC*/
|
||||
};
|
||||
|
||||
static struct lpc_ich_info lpc_chipset_info[] = {
|
||||
@ -554,6 +556,14 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
||||
.iTCO_version = 5,
|
||||
.spi_type = INTEL_SPI_BXT,
|
||||
},
|
||||
[LPC_GLK] = {
|
||||
.name = "Gemini Lake SoC",
|
||||
.spi_type = INTEL_SPI_BXT,
|
||||
},
|
||||
[LPC_COUGARMOUNTAIN] = {
|
||||
.name = "Cougar Mountain SoC",
|
||||
.iTCO_version = 3,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -682,6 +692,8 @@ static const struct pci_device_id lpc_ich_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9},
|
||||
{ PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M},
|
||||
{ PCI_VDEVICE(INTEL, 0x3197), LPC_GLK},
|
||||
{ PCI_VDEVICE(INTEL, 0x2b9c), LPC_COUGARMOUNTAIN},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10},
|
||||
|
@ -1022,9 +1022,7 @@ static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
|
||||
static void menelaus_rtc_update_work(struct menelaus_chip *m)
|
||||
{
|
||||
/* report 1/sec update */
|
||||
local_irq_disable();
|
||||
rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
|
||||
@ -1086,9 +1084,7 @@ static const struct rtc_class_ops menelaus_rtc_ops = {
|
||||
static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
|
||||
{
|
||||
/* report alarm */
|
||||
local_irq_disable();
|
||||
rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
local_irq_enable();
|
||||
|
||||
/* then disable it; alarms are oneshot */
|
||||
the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#define CPCAP_NR_IRQ_REG_BANKS 6
|
||||
#define CPCAP_NR_IRQ_CHIPS 3
|
||||
#define CPCAP_REGISTER_SIZE 4
|
||||
#define CPCAP_REGISTER_BITS 16
|
||||
|
||||
struct cpcap_ddata {
|
||||
struct spi_device *spi;
|
||||
@ -32,6 +34,32 @@ struct cpcap_ddata {
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int cpcap_sense_irq(struct regmap *regmap, int irq)
|
||||
{
|
||||
int regnum = irq / CPCAP_REGISTER_BITS;
|
||||
int mask = BIT(irq % CPCAP_REGISTER_BITS);
|
||||
int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE);
|
||||
int err, val;
|
||||
|
||||
if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4)
|
||||
return -EINVAL;
|
||||
|
||||
err = regmap_read(regmap, reg, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return !!(val & mask);
|
||||
}
|
||||
|
||||
int cpcap_sense_virq(struct regmap *regmap, int virq)
|
||||
{
|
||||
struct regmap_irq_chip_data *d = irq_get_chip_data(virq);
|
||||
int irq_base = regmap_irq_chip_get_base(d);
|
||||
|
||||
return cpcap_sense_irq(regmap, virq - irq_base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpcap_sense_virq);
|
||||
|
||||
static int cpcap_check_revision(struct cpcap_ddata *cpcap)
|
||||
{
|
||||
u16 vendor, rev;
|
||||
@ -71,6 +99,7 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
|
||||
.ack_base = CPCAP_REG_MI1,
|
||||
.mask_base = CPCAP_REG_MIM1,
|
||||
.use_ack = true,
|
||||
.ack_invert = true,
|
||||
},
|
||||
{
|
||||
.name = "cpcap-m2",
|
||||
@ -79,6 +108,7 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
|
||||
.ack_base = CPCAP_REG_MI2,
|
||||
.mask_base = CPCAP_REG_MIM2,
|
||||
.use_ack = true,
|
||||
.ack_invert = true,
|
||||
},
|
||||
{
|
||||
.name = "cpcap1-4",
|
||||
@ -86,8 +116,8 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
|
||||
.status_base = CPCAP_REG_INT1,
|
||||
.ack_base = CPCAP_REG_INT1,
|
||||
.mask_base = CPCAP_REG_INTM1,
|
||||
.type_base = CPCAP_REG_INTS1,
|
||||
.use_ack = true,
|
||||
.ack_invert = true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -126,7 +156,7 @@ static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
|
||||
cpcap->spi->irq,
|
||||
IRQF_TRIGGER_RISING |
|
||||
irq_get_trigger_type(cpcap->spi->irq) |
|
||||
IRQF_SHARED, -1,
|
||||
chip, &cpcap->irqdata[irq_chip]);
|
||||
if (ret) {
|
||||
|
@ -47,8 +47,7 @@ static const struct mfd_cell mt6323_devs[] = {
|
||||
{
|
||||
.name = "mt6323-regulator",
|
||||
.of_compatible = "mediatek,mt6323-regulator"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
.name = "mt6323-led",
|
||||
.of_compatible = "mediatek,mt6323-led"
|
||||
},
|
||||
|
267
drivers/mfd/mxs-lradc.c
Normal file
267
drivers/mfd/mxs-lradc.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Freescale MXS Low Resolution Analog-to-Digital Converter driver
|
||||
*
|
||||
* Copyright (c) 2012 DENX Software Engineering, GmbH.
|
||||
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* Authors:
|
||||
* Marek Vasut <marex@denx.de>
|
||||
* Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mxs-lradc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define ADC_CELL 0
|
||||
#define TSC_CELL 1
|
||||
#define RES_MEM 0
|
||||
|
||||
enum mx23_lradc_irqs {
|
||||
MX23_LRADC_TS_IRQ = 0,
|
||||
MX23_LRADC_CH0_IRQ,
|
||||
MX23_LRADC_CH1_IRQ,
|
||||
MX23_LRADC_CH2_IRQ,
|
||||
MX23_LRADC_CH3_IRQ,
|
||||
MX23_LRADC_CH4_IRQ,
|
||||
MX23_LRADC_CH5_IRQ,
|
||||
MX23_LRADC_CH6_IRQ,
|
||||
MX23_LRADC_CH7_IRQ,
|
||||
};
|
||||
|
||||
enum mx28_lradc_irqs {
|
||||
MX28_LRADC_TS_IRQ = 0,
|
||||
MX28_LRADC_TRESH0_IRQ,
|
||||
MX28_LRADC_TRESH1_IRQ,
|
||||
MX28_LRADC_CH0_IRQ,
|
||||
MX28_LRADC_CH1_IRQ,
|
||||
MX28_LRADC_CH2_IRQ,
|
||||
MX28_LRADC_CH3_IRQ,
|
||||
MX28_LRADC_CH4_IRQ,
|
||||
MX28_LRADC_CH5_IRQ,
|
||||
MX28_LRADC_CH6_IRQ,
|
||||
MX28_LRADC_CH7_IRQ,
|
||||
MX28_LRADC_BUTTON0_IRQ,
|
||||
MX28_LRADC_BUTTON1_IRQ,
|
||||
};
|
||||
|
||||
static struct resource mx23_adc_resources[] = {
|
||||
DEFINE_RES_MEM(0x0, 0x0),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH0_IRQ, "mxs-lradc-channel0"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH1_IRQ, "mxs-lradc-channel1"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH2_IRQ, "mxs-lradc-channel2"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH3_IRQ, "mxs-lradc-channel3"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH4_IRQ, "mxs-lradc-channel4"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH5_IRQ, "mxs-lradc-channel5"),
|
||||
};
|
||||
|
||||
static struct resource mx23_touchscreen_resources[] = {
|
||||
DEFINE_RES_MEM(0x0, 0x0),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_TS_IRQ, "mxs-lradc-touchscreen"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH6_IRQ, "mxs-lradc-channel6"),
|
||||
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH7_IRQ, "mxs-lradc-channel7"),
|
||||
};
|
||||
|
||||
static struct resource mx28_adc_resources[] = {
|
||||
DEFINE_RES_MEM(0x0, 0x0),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH0_IRQ, "mxs-lradc-thresh0"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH1_IRQ, "mxs-lradc-thresh1"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH0_IRQ, "mxs-lradc-channel0"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH1_IRQ, "mxs-lradc-channel1"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH2_IRQ, "mxs-lradc-channel2"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH3_IRQ, "mxs-lradc-channel3"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH4_IRQ, "mxs-lradc-channel4"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH5_IRQ, "mxs-lradc-channel5"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON0_IRQ, "mxs-lradc-button0"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON1_IRQ, "mxs-lradc-button1"),
|
||||
};
|
||||
|
||||
static struct resource mx28_touchscreen_resources[] = {
|
||||
DEFINE_RES_MEM(0x0, 0x0),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TS_IRQ, "mxs-lradc-touchscreen"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH6_IRQ, "mxs-lradc-channel6"),
|
||||
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH7_IRQ, "mxs-lradc-channel7"),
|
||||
};
|
||||
|
||||
static struct mfd_cell mx23_cells[] = {
|
||||
{
|
||||
.name = "mxs-lradc-adc",
|
||||
.resources = mx23_adc_resources,
|
||||
.num_resources = ARRAY_SIZE(mx23_adc_resources),
|
||||
},
|
||||
{
|
||||
.name = "mxs-lradc-ts",
|
||||
.resources = mx23_touchscreen_resources,
|
||||
.num_resources = ARRAY_SIZE(mx23_touchscreen_resources),
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell mx28_cells[] = {
|
||||
{
|
||||
.name = "mxs-lradc-adc",
|
||||
.resources = mx28_adc_resources,
|
||||
.num_resources = ARRAY_SIZE(mx28_adc_resources),
|
||||
},
|
||||
{
|
||||
.name = "mxs-lradc-ts",
|
||||
.resources = mx28_touchscreen_resources,
|
||||
.num_resources = ARRAY_SIZE(mx28_touchscreen_resources),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id mxs_lradc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
|
||||
{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
|
||||
|
||||
static int mxs_lradc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct mxs_lradc *lradc;
|
||||
struct mfd_cell *cells = NULL;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
u32 ts_wires = 0;
|
||||
|
||||
lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
|
||||
if (!lradc)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(mxs_lradc_dt_ids, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
lradc->soc = (enum mxs_lradc_id)of_id->data;
|
||||
|
||||
lradc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(lradc->clk)) {
|
||||
dev_err(dev, "Failed to get the delay unit clock\n");
|
||||
return PTR_ERR(lradc->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(lradc->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable the delay unit clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
|
||||
&ts_wires);
|
||||
|
||||
if (!ret) {
|
||||
lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
|
||||
|
||||
switch (ts_wires) {
|
||||
case 4:
|
||||
lradc->touchscreen_wire = MXS_LRADC_TOUCHSCREEN_4WIRE;
|
||||
break;
|
||||
case 5:
|
||||
if (lradc->soc == IMX28_LRADC) {
|
||||
lradc->touchscreen_wire =
|
||||
MXS_LRADC_TOUCHSCREEN_5WIRE;
|
||||
break;
|
||||
}
|
||||
/* fall through to an error message for i.MX23 */
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"Unsupported number of touchscreen wires (%d)\n"
|
||||
, ts_wires);
|
||||
ret = -EINVAL;
|
||||
goto err_clk;
|
||||
}
|
||||
} else {
|
||||
lradc->buffer_vchans = BUFFER_VCHANS_ALL;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, lradc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (lradc->soc) {
|
||||
case IMX23_LRADC:
|
||||
mx23_adc_resources[RES_MEM] = *res;
|
||||
mx23_touchscreen_resources[RES_MEM] = *res;
|
||||
cells = mx23_cells;
|
||||
break;
|
||||
case IMX28_LRADC:
|
||||
mx28_adc_resources[RES_MEM] = *res;
|
||||
mx28_touchscreen_resources[RES_MEM] = *res;
|
||||
cells = mx28_cells;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported SoC\n");
|
||||
ret = -ENODEV;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||
&cells[ADC_CELL], 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
if (!lradc->touchscreen_wire)
|
||||
return 0;
|
||||
|
||||
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||
&cells[TSC_CELL], 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to add the touchscreen subdevice\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(lradc->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_lradc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mxs_lradc *lradc = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(lradc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mxs_lradc_driver = {
|
||||
.driver = {
|
||||
.name = "mxs-lradc",
|
||||
.of_match_table = mxs_lradc_dt_ids,
|
||||
},
|
||||
.probe = mxs_lradc_probe,
|
||||
.remove = mxs_lradc_remove,
|
||||
};
|
||||
module_platform_driver(mxs_lradc_driver);
|
||||
|
||||
MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mxs-lradc");
|
@ -373,12 +373,13 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata)
|
||||
} else if (pdata->port_mode[i] ==
|
||||
OMAP_EHCI_PORT_MODE_TLL) {
|
||||
/*
|
||||
* Disable AutoIdle, BitStuffing
|
||||
* and use SDR Mode
|
||||
* Disable UTMI AutoIdle, BitStuffing
|
||||
* and use SDR Mode. Enable ULPI AutoIdle.
|
||||
*/
|
||||
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
|
||||
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
|
||||
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
|
||||
reg |= OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
|
||||
reg |= OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE;
|
||||
} else if (pdata->port_mode[i] ==
|
||||
OMAP_EHCI_PORT_MODE_HSIC) {
|
||||
/*
|
||||
|
@ -430,6 +430,20 @@ static void palmas_power_off(void)
|
||||
{
|
||||
unsigned int addr;
|
||||
int ret, slave;
|
||||
struct device_node *np = palmas_dev->dev->of_node;
|
||||
|
||||
if (of_property_read_bool(np, "ti,palmas-override-powerhold")) {
|
||||
addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
|
||||
PALMAS_PRIMARY_SECONDARY_PAD2);
|
||||
slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
|
||||
|
||||
ret = regmap_update_bits(palmas_dev->regmap[slave], addr,
|
||||
PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK, 0);
|
||||
if (ret)
|
||||
dev_err(palmas_dev->dev,
|
||||
"Unable to write PRIMARY_SECONDARY_PAD2 %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
|
||||
addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL);
|
||||
@ -567,7 +581,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
|
||||
PALMAS_POLARITY_CTRL, PALMAS_POLARITY_CTRL_INT_POLARITY,
|
||||
reg);
|
||||
if (ret < 0) {
|
||||
dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret);
|
||||
dev_err(palmas->dev, "POLARITY_CTRL update failed: %d\n", ret);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
|
@ -927,7 +927,7 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
|
||||
|
||||
static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr)
|
||||
{
|
||||
dev_info(&(pcr->pci->dev), "%s: pcr->msi_en = %d, pci->irq = %d\n",
|
||||
pcr_dbg(pcr, "%s: pcr->msi_en = %d, pci->irq = %d\n",
|
||||
__func__, pcr->msi_en, pcr->pci->irq);
|
||||
|
||||
if (request_irq(pcr->pci->irq, rtsx_pci_isr,
|
||||
|
@ -60,8 +60,8 @@ static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev)
|
||||
struct sta2x11_mfd *mfd;
|
||||
|
||||
if (!pdev && !list_empty(&sta2x11_mfd_list)) {
|
||||
pr_warning("%s: Unspecified device, "
|
||||
"using first instance\n", __func__);
|
||||
pr_warn("%s: Unspecified device, using first instance\n",
|
||||
__func__);
|
||||
return list_entry(sta2x11_mfd_list.next,
|
||||
struct sta2x11_mfd, list);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ static const struct regmap_config stm32_timers_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = sizeof(u32),
|
||||
.max_register = 0x400,
|
||||
.max_register = 0x3fc,
|
||||
};
|
||||
|
||||
static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
|
||||
@ -61,6 +61,13 @@ static int stm32_timers_probe(struct platform_device *pdev)
|
||||
return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
}
|
||||
|
||||
static int stm32_timers_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_timers_of_match[] = {
|
||||
{ .compatible = "st,stm32-timers", },
|
||||
{ /* end node */ },
|
||||
@ -69,6 +76,7 @@ MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
|
||||
|
||||
static struct platform_driver stm32_timers_driver = {
|
||||
.probe = stm32_timers_probe,
|
||||
.remove = stm32_timers_remove,
|
||||
.driver = {
|
||||
.name = "stm32-timers",
|
||||
.of_match_table = stm32_timers_of_match,
|
||||
|
@ -568,6 +568,8 @@ static const u8 stmpe1600_regs[] = {
|
||||
[STMPE_IDX_GPMR_CSB] = STMPE1600_REG_GPMR_MSB,
|
||||
[STMPE_IDX_GPSR_LSB] = STMPE1600_REG_GPSR_LSB,
|
||||
[STMPE_IDX_GPSR_CSB] = STMPE1600_REG_GPSR_MSB,
|
||||
[STMPE_IDX_GPCR_LSB] = STMPE1600_REG_GPSR_LSB,
|
||||
[STMPE_IDX_GPCR_CSB] = STMPE1600_REG_GPSR_MSB,
|
||||
[STMPE_IDX_GPDR_LSB] = STMPE1600_REG_GPDR_LSB,
|
||||
[STMPE_IDX_GPDR_CSB] = STMPE1600_REG_GPDR_MSB,
|
||||
[STMPE_IDX_IEGPIOR_LSB] = STMPE1600_REG_IEGPIOR_LSB,
|
||||
|
@ -69,7 +69,7 @@ static const struct resource t7l66xb_mmc_resources[] = {
|
||||
struct t7l66xb {
|
||||
void __iomem *scr;
|
||||
/* Lock to protect registers requiring read/modify/write ops. */
|
||||
spinlock_t lock;
|
||||
raw_spinlock_t lock;
|
||||
|
||||
struct resource rscr;
|
||||
struct clk *clk48m;
|
||||
@ -89,13 +89,13 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc)
|
||||
|
||||
clk_prepare_enable(t7l66xb->clk32k);
|
||||
|
||||
spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
|
||||
dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
|
||||
dev_ctl |= SCR_DEV_CTL_MMC;
|
||||
tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
|
||||
tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
|
||||
t7l66xb_mmc_resources[0].start & 0xfffe);
|
||||
@ -110,13 +110,13 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc)
|
||||
unsigned long flags;
|
||||
u8 dev_ctl;
|
||||
|
||||
spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
|
||||
dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
|
||||
dev_ctl &= ~SCR_DEV_CTL_MMC;
|
||||
tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
|
||||
clk_disable_unprepare(t7l66xb->clk32k);
|
||||
|
||||
@ -206,11 +206,11 @@ static void t7l66xb_irq_mask(struct irq_data *data)
|
||||
unsigned long flags;
|
||||
u8 imr;
|
||||
|
||||
spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
|
||||
imr |= 1 << (data->irq - t7l66xb->irq_base);
|
||||
tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
|
||||
spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
}
|
||||
|
||||
static void t7l66xb_irq_unmask(struct irq_data *data)
|
||||
@ -219,11 +219,11 @@ static void t7l66xb_irq_unmask(struct irq_data *data)
|
||||
unsigned long flags;
|
||||
u8 imr;
|
||||
|
||||
spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&t7l66xb->lock, flags);
|
||||
imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
|
||||
imr &= ~(1 << (data->irq - t7l66xb->irq_base));
|
||||
tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
|
||||
spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&t7l66xb->lock, flags);
|
||||
}
|
||||
|
||||
static struct irq_chip t7l66xb_chip = {
|
||||
@ -321,7 +321,7 @@ static int t7l66xb_probe(struct platform_device *dev)
|
||||
if (!t7l66xb)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&t7l66xb->lock);
|
||||
raw_spin_lock_init(&t7l66xb->lock);
|
||||
|
||||
platform_set_drvdata(dev, t7l66xb);
|
||||
|
||||
|
@ -95,7 +95,7 @@ struct tc6393xb {
|
||||
|
||||
struct clk *clk; /* 3,6 Mhz */
|
||||
|
||||
spinlock_t lock; /* protects RMW cycles */
|
||||
raw_spinlock_t lock; /* protects RMW cycles */
|
||||
|
||||
struct {
|
||||
u8 fer;
|
||||
@ -126,13 +126,13 @@ static int tc6393xb_nand_enable(struct platform_device *nand)
|
||||
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
/* SMD buffer on */
|
||||
dev_dbg(&dev->dev, "SMD buffer on\n");
|
||||
tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1));
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -226,7 +226,7 @@ static int tc6393xb_ohci_enable(struct platform_device *dev)
|
||||
u16 ccr;
|
||||
u8 fer;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
|
||||
ccr |= SCR_CCR_USBCK;
|
||||
@ -236,7 +236,7 @@ static int tc6393xb_ohci_enable(struct platform_device *dev)
|
||||
fer |= SCR_FER_USBEN;
|
||||
tmio_iowrite8(fer, tc6393xb->scr + SCR_FER);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -248,7 +248,7 @@ static int tc6393xb_ohci_disable(struct platform_device *dev)
|
||||
u16 ccr;
|
||||
u8 fer;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
fer = tmio_ioread8(tc6393xb->scr + SCR_FER);
|
||||
fer &= ~SCR_FER_USBEN;
|
||||
@ -258,7 +258,7 @@ static int tc6393xb_ohci_disable(struct platform_device *dev)
|
||||
ccr &= ~SCR_CCR_USBCK;
|
||||
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -280,14 +280,14 @@ static int tc6393xb_fb_enable(struct platform_device *dev)
|
||||
unsigned long flags;
|
||||
u16 ccr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
|
||||
ccr &= ~SCR_CCR_MCLK_MASK;
|
||||
ccr |= SCR_CCR_MCLK_48;
|
||||
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -298,14 +298,14 @@ static int tc6393xb_fb_disable(struct platform_device *dev)
|
||||
unsigned long flags;
|
||||
u16 ccr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
|
||||
ccr &= ~SCR_CCR_MCLK_MASK;
|
||||
ccr |= SCR_CCR_MCLK_OFF;
|
||||
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -317,7 +317,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
|
||||
u8 fer;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
fer = ioread8(tc6393xb->scr + SCR_FER);
|
||||
if (on)
|
||||
@ -326,7 +326,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
|
||||
fer &= ~SCR_FER_SLCDEN;
|
||||
iowrite8(fer, tc6393xb->scr + SCR_FER);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -338,12 +338,12 @@ int tc6393xb_lcd_mode(struct platform_device *fb,
|
||||
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
iowrite16(mode->pixclock, tc6393xb->scr + SCR_PLL1CR + 0);
|
||||
iowrite16(mode->pixclock >> 16, tc6393xb->scr + SCR_PLL1CR + 2);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -462,11 +462,11 @@ static void tc6393xb_gpio_set(struct gpio_chip *chip,
|
||||
struct tc6393xb *tc6393xb = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
__tc6393xb_gpio_set(chip, offset, value);
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
}
|
||||
|
||||
static int tc6393xb_gpio_direction_input(struct gpio_chip *chip,
|
||||
@ -476,13 +476,13 @@ static int tc6393xb_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned long flags;
|
||||
u8 doecr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
|
||||
doecr &= ~TC_GPIO_BIT(offset);
|
||||
tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -494,7 +494,7 @@ static int tc6393xb_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned long flags;
|
||||
u8 doecr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
|
||||
__tc6393xb_gpio_set(chip, offset, value);
|
||||
|
||||
@ -502,7 +502,7 @@ static int tc6393xb_gpio_direction_output(struct gpio_chip *chip,
|
||||
doecr |= TC_GPIO_BIT(offset);
|
||||
tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
|
||||
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -548,11 +548,11 @@ static void tc6393xb_irq_mask(struct irq_data *data)
|
||||
unsigned long flags;
|
||||
u8 imr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
imr = tmio_ioread8(tc6393xb->scr + SCR_IMR);
|
||||
imr |= 1 << (data->irq - tc6393xb->irq_base);
|
||||
tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR);
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
}
|
||||
|
||||
static void tc6393xb_irq_unmask(struct irq_data *data)
|
||||
@ -561,11 +561,11 @@ static void tc6393xb_irq_unmask(struct irq_data *data)
|
||||
unsigned long flags;
|
||||
u8 imr;
|
||||
|
||||
spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
|
||||
imr = tmio_ioread8(tc6393xb->scr + SCR_IMR);
|
||||
imr &= ~(1 << (data->irq - tc6393xb->irq_base));
|
||||
tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR);
|
||||
spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
|
||||
}
|
||||
|
||||
static struct irq_chip tc6393xb_chip = {
|
||||
@ -628,7 +628,7 @@ static int tc6393xb_probe(struct platform_device *dev)
|
||||
goto err_kzalloc;
|
||||
}
|
||||
|
||||
spin_lock_init(&tc6393xb->lock);
|
||||
raw_spin_lock_init(&tc6393xb->lock);
|
||||
|
||||
platform_set_drvdata(dev, tc6393xb);
|
||||
|
||||
|
259
drivers/mfd/ti-lmu.c
Normal file
259
drivers/mfd/ti-lmu.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* TI LMU (Lighting Management Unit) Core Driver
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/ti-lmu.h>
|
||||
#include <linux/mfd/ti-lmu-register.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct ti_lmu_data {
|
||||
struct mfd_cell *cells;
|
||||
int num_cells;
|
||||
unsigned int max_register;
|
||||
};
|
||||
|
||||
static int ti_lmu_enable_hw(struct ti_lmu *lmu, enum ti_lmu_id id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (gpio_is_valid(lmu->en_gpio)) {
|
||||
ret = devm_gpio_request_one(lmu->dev, lmu->en_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "lmu_hwen");
|
||||
if (ret) {
|
||||
dev_err(lmu->dev, "Can not request enable GPIO: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delay about 1ms after HW enable pin control */
|
||||
usleep_range(1000, 1500);
|
||||
|
||||
/* LM3631 has additional power up sequence - enable LCD_EN bit. */
|
||||
if (id == LM3631) {
|
||||
return regmap_update_bits(lmu->regmap, LM3631_REG_DEVCTRL,
|
||||
LM3631_LCD_EN_MASK,
|
||||
LM3631_LCD_EN_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ti_lmu_disable_hw(struct ti_lmu *lmu)
|
||||
{
|
||||
if (gpio_is_valid(lmu->en_gpio))
|
||||
gpio_set_value(lmu->en_gpio, 0);
|
||||
}
|
||||
|
||||
static struct mfd_cell lm3532_devices[] = {
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3532,
|
||||
.of_compatible = "ti,lm3532-backlight",
|
||||
},
|
||||
};
|
||||
|
||||
#define LM363X_REGULATOR(_id) \
|
||||
{ \
|
||||
.name = "lm363x-regulator", \
|
||||
.id = _id, \
|
||||
.of_compatible = "ti,lm363x-regulator", \
|
||||
} \
|
||||
|
||||
static struct mfd_cell lm3631_devices[] = {
|
||||
LM363X_REGULATOR(LM3631_BOOST),
|
||||
LM363X_REGULATOR(LM3631_LDO_CONT),
|
||||
LM363X_REGULATOR(LM3631_LDO_OREF),
|
||||
LM363X_REGULATOR(LM3631_LDO_POS),
|
||||
LM363X_REGULATOR(LM3631_LDO_NEG),
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3631,
|
||||
.of_compatible = "ti,lm3631-backlight",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3632_devices[] = {
|
||||
LM363X_REGULATOR(LM3632_BOOST),
|
||||
LM363X_REGULATOR(LM3632_LDO_POS),
|
||||
LM363X_REGULATOR(LM3632_LDO_NEG),
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3632,
|
||||
.of_compatible = "ti,lm3632-backlight",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3633_devices[] = {
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3633,
|
||||
.of_compatible = "ti,lm3633-backlight",
|
||||
},
|
||||
{
|
||||
.name = "lm3633-leds",
|
||||
.of_compatible = "ti,lm3633-leds",
|
||||
},
|
||||
/* Monitoring driver for open/short circuit detection */
|
||||
{
|
||||
.name = "ti-lmu-fault-monitor",
|
||||
.id = LM3633,
|
||||
.of_compatible = "ti,lm3633-fault-monitor",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3695_devices[] = {
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3695,
|
||||
.of_compatible = "ti,lm3695-backlight",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3697_devices[] = {
|
||||
{
|
||||
.name = "ti-lmu-backlight",
|
||||
.id = LM3697,
|
||||
.of_compatible = "ti,lm3697-backlight",
|
||||
},
|
||||
/* Monitoring driver for open/short circuit detection */
|
||||
{
|
||||
.name = "ti-lmu-fault-monitor",
|
||||
.id = LM3697,
|
||||
.of_compatible = "ti,lm3697-fault-monitor",
|
||||
},
|
||||
};
|
||||
|
||||
#define TI_LMU_DATA(chip, max_reg) \
|
||||
static const struct ti_lmu_data chip##_data = \
|
||||
{ \
|
||||
.cells = chip##_devices, \
|
||||
.num_cells = ARRAY_SIZE(chip##_devices),\
|
||||
.max_register = max_reg, \
|
||||
} \
|
||||
|
||||
TI_LMU_DATA(lm3532, LM3532_MAX_REG);
|
||||
TI_LMU_DATA(lm3631, LM3631_MAX_REG);
|
||||
TI_LMU_DATA(lm3632, LM3632_MAX_REG);
|
||||
TI_LMU_DATA(lm3633, LM3633_MAX_REG);
|
||||
TI_LMU_DATA(lm3695, LM3695_MAX_REG);
|
||||
TI_LMU_DATA(lm3697, LM3697_MAX_REG);
|
||||
|
||||
static const struct of_device_id ti_lmu_of_match[] = {
|
||||
{ .compatible = "ti,lm3532", .data = &lm3532_data },
|
||||
{ .compatible = "ti,lm3631", .data = &lm3631_data },
|
||||
{ .compatible = "ti,lm3632", .data = &lm3632_data },
|
||||
{ .compatible = "ti,lm3633", .data = &lm3633_data },
|
||||
{ .compatible = "ti,lm3695", .data = &lm3695_data },
|
||||
{ .compatible = "ti,lm3697", .data = &lm3697_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_lmu_of_match);
|
||||
|
||||
static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &cl->dev;
|
||||
const struct of_device_id *match;
|
||||
const struct ti_lmu_data *data;
|
||||
struct regmap_config regmap_cfg;
|
||||
struct ti_lmu *lmu;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(ti_lmu_of_match, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
/*
|
||||
* Get device specific data from of_match table.
|
||||
* This data is defined by using TI_LMU_DATA() macro.
|
||||
*/
|
||||
data = (struct ti_lmu_data *)match->data;
|
||||
|
||||
lmu = devm_kzalloc(dev, sizeof(*lmu), GFP_KERNEL);
|
||||
if (!lmu)
|
||||
return -ENOMEM;
|
||||
|
||||
lmu->dev = &cl->dev;
|
||||
|
||||
/* Setup regmap */
|
||||
memset(®map_cfg, 0, sizeof(struct regmap_config));
|
||||
regmap_cfg.reg_bits = 8;
|
||||
regmap_cfg.val_bits = 8;
|
||||
regmap_cfg.name = id->name;
|
||||
regmap_cfg.max_register = data->max_register;
|
||||
|
||||
lmu->regmap = devm_regmap_init_i2c(cl, ®map_cfg);
|
||||
if (IS_ERR(lmu->regmap))
|
||||
return PTR_ERR(lmu->regmap);
|
||||
|
||||
/* HW enable pin control and additional power up sequence if required */
|
||||
lmu->en_gpio = of_get_named_gpio(dev->of_node, "enable-gpios", 0);
|
||||
ret = ti_lmu_enable_hw(lmu, id->driver_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Fault circuit(open/short) can be detected by ti-lmu-fault-monitor.
|
||||
* After fault detection is done, some devices should re-initialize
|
||||
* configuration. The notifier enables such kind of handling.
|
||||
*/
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&lmu->notifier);
|
||||
|
||||
i2c_set_clientdata(cl, lmu);
|
||||
|
||||
return mfd_add_devices(lmu->dev, 0, data->cells,
|
||||
data->num_cells, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static int ti_lmu_remove(struct i2c_client *cl)
|
||||
{
|
||||
struct ti_lmu *lmu = i2c_get_clientdata(cl);
|
||||
|
||||
ti_lmu_disable_hw(lmu);
|
||||
mfd_remove_devices(lmu->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ti_lmu_ids[] = {
|
||||
{ "lm3532", LM3532 },
|
||||
{ "lm3631", LM3631 },
|
||||
{ "lm3632", LM3632 },
|
||||
{ "lm3633", LM3633 },
|
||||
{ "lm3695", LM3695 },
|
||||
{ "lm3697", LM3697 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ti_lmu_ids);
|
||||
|
||||
static struct i2c_driver ti_lmu_driver = {
|
||||
.probe = ti_lmu_probe,
|
||||
.remove = ti_lmu_remove,
|
||||
.driver = {
|
||||
.name = "ti-lmu",
|
||||
.of_match_table = ti_lmu_of_match,
|
||||
},
|
||||
.id_table = ti_lmu_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(ti_lmu_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TI LMU MFD Core Driver");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -49,9 +49,9 @@ static int tps65912_spi_probe(struct spi_device *spi)
|
||||
return tps65912_device_init(tps);
|
||||
}
|
||||
|
||||
static int tps65912_spi_remove(struct spi_device *client)
|
||||
static int tps65912_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct tps65912 *tps = spi_get_drvdata(client);
|
||||
struct tps65912 *tps = spi_get_drvdata(spi);
|
||||
|
||||
return tps65912_device_exit(tps);
|
||||
}
|
||||
|
@ -502,9 +502,7 @@ static int load_twl4030_script(const struct twl4030_power_data *pdata,
|
||||
}
|
||||
if (tscript->flags & TWL4030_SLEEP_SCRIPT) {
|
||||
if (!order)
|
||||
pr_warning("TWL4030: Bad order of scripts (sleep "\
|
||||
"script before wakeup) Leads to boot"\
|
||||
"failure on some boards\n");
|
||||
pr_warn("TWL4030: Bad order of scripts (sleep script before wakeup) Leads to boot failure on some boards\n");
|
||||
err = twl4030_config_sleep_sequence(address);
|
||||
}
|
||||
out:
|
||||
@ -701,6 +699,7 @@ static struct twl4030_ins omap3_wrst_seq[] = {
|
||||
TWL_RESOURCE_RESET(RES_MAIN_REF),
|
||||
TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2),
|
||||
TWL_RESOURCE_RESET(RES_VUSB_3V1),
|
||||
TWL_RESOURCE_RESET(RES_VMMC1),
|
||||
TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1),
|
||||
TWL_RESOURCE_GROUP_RESET(RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0),
|
||||
TWL_RESOURCE_ON(RES_RESET),
|
||||
@ -929,8 +928,7 @@ static int twl4030_power_probe(struct platform_device *pdev)
|
||||
err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
|
||||
TWL4030_PM_MASTER_CFG_P123_TRANSITION);
|
||||
if (err) {
|
||||
pr_warning("TWL4030 Unable to read registers\n");
|
||||
|
||||
pr_warn("TWL4030 Unable to read registers\n");
|
||||
} else if (!(val & SEQ_OFFSYNC)) {
|
||||
val |= SEQ_OFFSYNC;
|
||||
err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val,
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
#include <linux/mfd/wm831x/pdata.h>
|
||||
@ -1613,12 +1615,24 @@ struct regmap_config wm831x_regmap_config = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(wm831x_regmap_config);
|
||||
|
||||
const struct of_device_id wm831x_of_match[] = {
|
||||
{ .compatible = "wlf,wm8310", .data = (void *)WM8310 },
|
||||
{ .compatible = "wlf,wm8311", .data = (void *)WM8311 },
|
||||
{ .compatible = "wlf,wm8312", .data = (void *)WM8312 },
|
||||
{ .compatible = "wlf,wm8320", .data = (void *)WM8320 },
|
||||
{ .compatible = "wlf,wm8321", .data = (void *)WM8321 },
|
||||
{ .compatible = "wlf,wm8325", .data = (void *)WM8325 },
|
||||
{ .compatible = "wlf,wm8326", .data = (void *)WM8326 },
|
||||
{ },
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(wm831x_of_match);
|
||||
|
||||
/*
|
||||
* Instantiate the generic non-control parts of the device.
|
||||
*/
|
||||
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
int wm831x_device_init(struct wm831x *wm831x, int irq)
|
||||
{
|
||||
struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
|
||||
struct wm831x_pdata *pdata = &wm831x->pdata;
|
||||
int rev, wm831x_num;
|
||||
enum wm831x_parent parent;
|
||||
int ret, i;
|
||||
@ -1627,8 +1641,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
mutex_init(&wm831x->key_lock);
|
||||
dev_set_drvdata(wm831x->dev, wm831x);
|
||||
|
||||
if (pdata)
|
||||
wm831x->soft_shutdown = pdata->soft_shutdown;
|
||||
wm831x->soft_shutdown = pdata->soft_shutdown;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
|
||||
if (ret < 0) {
|
||||
@ -1663,7 +1676,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
*/
|
||||
if (ret == 0) {
|
||||
dev_info(wm831x->dev, "Device is an engineering sample\n");
|
||||
ret = id;
|
||||
ret = wm831x->type;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
@ -1736,9 +1749,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
/* This will need revisiting in future but is OK for all
|
||||
* current parts.
|
||||
*/
|
||||
if (parent != id)
|
||||
dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
|
||||
id);
|
||||
if (parent != wm831x->type)
|
||||
dev_warn(wm831x->dev, "Device was registered as a WM%x\n",
|
||||
wm831x->type);
|
||||
|
||||
/* Bootstrap the user key */
|
||||
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
@ -27,15 +29,26 @@
|
||||
static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wm831x_pdata *pdata = dev_get_platdata(&i2c->dev);
|
||||
const struct of_device_id *of_id;
|
||||
struct wm831x *wm831x;
|
||||
enum wm831x_parent type;
|
||||
int ret;
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
of_id = of_match_device(wm831x_of_match, &i2c->dev);
|
||||
type = (enum wm831x_parent)of_id->data;
|
||||
} else {
|
||||
type = (enum wm831x_parent)id->driver_data;
|
||||
}
|
||||
|
||||
wm831x = devm_kzalloc(&i2c->dev, sizeof(struct wm831x), GFP_KERNEL);
|
||||
if (wm831x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, wm831x);
|
||||
wm831x->dev = &i2c->dev;
|
||||
wm831x->type = type;
|
||||
|
||||
wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config);
|
||||
if (IS_ERR(wm831x->regmap)) {
|
||||
@ -45,7 +58,10 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
|
||||
if (pdata)
|
||||
memcpy(&wm831x->pdata, pdata, sizeof(*pdata));
|
||||
|
||||
return wm831x_device_init(wm831x, i2c->irq);
|
||||
}
|
||||
|
||||
static int wm831x_i2c_remove(struct i2c_client *i2c)
|
||||
@ -94,6 +110,7 @@ static struct i2c_driver wm831x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm831x",
|
||||
.pm = &wm831x_pm_ops,
|
||||
.of_match_table = of_match_ptr(wm831x_of_match),
|
||||
},
|
||||
.probe = wm831x_i2c_probe,
|
||||
.remove = wm831x_i2c_remove,
|
||||
|
@ -564,7 +564,7 @@ static const struct irq_domain_ops wm831x_irq_domain_ops = {
|
||||
|
||||
int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
{
|
||||
struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
|
||||
struct wm831x_pdata *pdata = &wm831x->pdata;
|
||||
struct irq_domain *domain;
|
||||
int i, ret, irq_base;
|
||||
|
||||
@ -579,7 +579,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
}
|
||||
|
||||
/* Try to dynamically allocate IRQs if no base is specified */
|
||||
if (pdata && pdata->irq_base) {
|
||||
if (pdata->irq_base) {
|
||||
irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
||||
WM831X_NUM_IRQS, 0);
|
||||
if (irq_base < 0) {
|
||||
@ -608,7 +608,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata && pdata->irq_cmos)
|
||||
if (pdata->irq_cmos)
|
||||
i = 0;
|
||||
else
|
||||
i = WM831X_IRQ_OD;
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -23,12 +25,19 @@
|
||||
|
||||
static int wm831x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct wm831x_pdata *pdata = dev_get_platdata(&spi->dev);
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct of_device_id *of_id;
|
||||
struct wm831x *wm831x;
|
||||
enum wm831x_parent type;
|
||||
int ret;
|
||||
|
||||
type = (enum wm831x_parent)id->driver_data;
|
||||
if (spi->dev.of_node) {
|
||||
of_id = of_match_device(wm831x_of_match, &spi->dev);
|
||||
type = (enum wm831x_parent)of_id->data;
|
||||
} else {
|
||||
type = (enum wm831x_parent)id->driver_data;
|
||||
}
|
||||
|
||||
wm831x = devm_kzalloc(&spi->dev, sizeof(struct wm831x), GFP_KERNEL);
|
||||
if (wm831x == NULL)
|
||||
@ -38,6 +47,7 @@ static int wm831x_spi_probe(struct spi_device *spi)
|
||||
|
||||
spi_set_drvdata(spi, wm831x);
|
||||
wm831x->dev = &spi->dev;
|
||||
wm831x->type = type;
|
||||
|
||||
wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config);
|
||||
if (IS_ERR(wm831x->regmap)) {
|
||||
@ -47,7 +57,10 @@ static int wm831x_spi_probe(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return wm831x_device_init(wm831x, type, spi->irq);
|
||||
if (pdata)
|
||||
memcpy(&wm831x->pdata, pdata, sizeof(*pdata));
|
||||
|
||||
return wm831x_device_init(wm831x, spi->irq);
|
||||
}
|
||||
|
||||
static int wm831x_spi_remove(struct spi_device *spi)
|
||||
@ -97,6 +110,7 @@ static struct spi_driver wm831x_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm831x",
|
||||
.pm = &wm831x_spi_pm,
|
||||
.of_match_table = of_match_ptr(wm831x_of_match),
|
||||
},
|
||||
.id_table = wm831x_spi_ids,
|
||||
.probe = wm831x_spi_probe,
|
||||
|
@ -1019,7 +1019,7 @@ config INTEL_PMC_IPC
|
||||
config INTEL_BXTWC_PMIC_TMU
|
||||
tristate "Intel BXT Whiskey Cove TMU Driver"
|
||||
depends on REGMAP
|
||||
depends on INTEL_SOC_PMIC && INTEL_PMC_IPC
|
||||
depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
|
||||
---help---
|
||||
Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
|
||||
This driver enables the alarm wakeup functionality in the TMU unit
|
||||
|
@ -353,7 +353,7 @@ endmenu
|
||||
|
||||
config INTEL_BXT_PMIC_THERMAL
|
||||
tristate "Intel Broxton PMIC thermal driver"
|
||||
depends on X86 && INTEL_SOC_PMIC && REGMAP
|
||||
depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP
|
||||
help
|
||||
Select this driver for Intel Broxton PMIC with ADC channels monitoring
|
||||
system temperature measurements and alerts.
|
||||
|
@ -20,6 +20,7 @@ enum axp20x_variants {
|
||||
AXP221_ID,
|
||||
AXP223_ID,
|
||||
AXP288_ID,
|
||||
AXP803_ID,
|
||||
AXP806_ID,
|
||||
AXP809_ID,
|
||||
NR_AXP20X_VARIANTS,
|
||||
@ -228,13 +229,13 @@ enum axp20x_variants {
|
||||
#define AXP20X_OCV_MAX 0xf
|
||||
|
||||
/* AXP22X specific registers */
|
||||
#define AXP22X_PMIC_ADC_H 0x56
|
||||
#define AXP22X_PMIC_ADC_L 0x57
|
||||
#define AXP22X_PMIC_TEMP_H 0x56
|
||||
#define AXP22X_PMIC_TEMP_L 0x57
|
||||
#define AXP22X_TS_ADC_H 0x58
|
||||
#define AXP22X_TS_ADC_L 0x59
|
||||
#define AXP22X_BATLOW_THRES1 0xe6
|
||||
|
||||
/* AXP288 specific registers */
|
||||
/* AXP288/AXP803 specific registers */
|
||||
#define AXP288_POWER_REASON 0x02
|
||||
#define AXP288_BC_GLOBAL 0x2c
|
||||
#define AXP288_BC_VBUS_CNTL 0x2d
|
||||
@ -475,6 +476,43 @@ enum axp288_irqs {
|
||||
AXP288_IRQ_BC_USB_CHNG,
|
||||
};
|
||||
|
||||
enum axp803_irqs {
|
||||
AXP803_IRQ_ACIN_OVER_V = 1,
|
||||
AXP803_IRQ_ACIN_PLUGIN,
|
||||
AXP803_IRQ_ACIN_REMOVAL,
|
||||
AXP803_IRQ_VBUS_OVER_V,
|
||||
AXP803_IRQ_VBUS_PLUGIN,
|
||||
AXP803_IRQ_VBUS_REMOVAL,
|
||||
AXP803_IRQ_BATT_PLUGIN,
|
||||
AXP803_IRQ_BATT_REMOVAL,
|
||||
AXP803_IRQ_BATT_ENT_ACT_MODE,
|
||||
AXP803_IRQ_BATT_EXIT_ACT_MODE,
|
||||
AXP803_IRQ_CHARG,
|
||||
AXP803_IRQ_CHARG_DONE,
|
||||
AXP803_IRQ_BATT_CHG_TEMP_HIGH,
|
||||
AXP803_IRQ_BATT_CHG_TEMP_HIGH_END,
|
||||
AXP803_IRQ_BATT_CHG_TEMP_LOW,
|
||||
AXP803_IRQ_BATT_CHG_TEMP_LOW_END,
|
||||
AXP803_IRQ_BATT_ACT_TEMP_HIGH,
|
||||
AXP803_IRQ_BATT_ACT_TEMP_HIGH_END,
|
||||
AXP803_IRQ_BATT_ACT_TEMP_LOW,
|
||||
AXP803_IRQ_BATT_ACT_TEMP_LOW_END,
|
||||
AXP803_IRQ_DIE_TEMP_HIGH,
|
||||
AXP803_IRQ_GPADC,
|
||||
AXP803_IRQ_LOW_PWR_LVL1,
|
||||
AXP803_IRQ_LOW_PWR_LVL2,
|
||||
AXP803_IRQ_TIMER,
|
||||
AXP803_IRQ_PEK_RIS_EDGE,
|
||||
AXP803_IRQ_PEK_FAL_EDGE,
|
||||
AXP803_IRQ_PEK_SHORT,
|
||||
AXP803_IRQ_PEK_LONG,
|
||||
AXP803_IRQ_PEK_OVER_OFF,
|
||||
AXP803_IRQ_GPIO1_INPUT,
|
||||
AXP803_IRQ_GPIO0_INPUT,
|
||||
AXP803_IRQ_BC_USB_CHNG,
|
||||
AXP803_IRQ_MV_CHNG,
|
||||
};
|
||||
|
||||
enum axp806_irqs {
|
||||
AXP806_IRQ_DIE_TEMP_HIGH_LV1,
|
||||
AXP806_IRQ_DIE_TEMP_HIGH_LV2,
|
||||
|
@ -305,4 +305,22 @@ extern struct attribute_group cros_ec_attr_group;
|
||||
extern struct attribute_group cros_ec_lightbar_attr_group;
|
||||
extern struct attribute_group cros_ec_vbc_attr_group;
|
||||
|
||||
/* ACPI GPE handler */
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
int cros_ec_acpi_install_gpe_handler(struct device *dev);
|
||||
void cros_ec_acpi_remove_gpe_handler(void);
|
||||
void cros_ec_acpi_clear_gpe(void);
|
||||
|
||||
#else /* CONFIG_ACPI */
|
||||
|
||||
static inline int cros_ec_acpi_install_gpe_handler(struct device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void cros_ec_acpi_remove_gpe_handler(void) {}
|
||||
static inline void cros_ec_acpi_clear_gpe(void) {}
|
||||
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
#endif /* __LINUX_MFD_CROS_EC_H */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dialog Semiconductor Ltd.
|
||||
* Copyright (C) 2015-2017 Dialog Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -18,7 +18,31 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/da9062/registers.h>
|
||||
|
||||
/* Interrupts */
|
||||
enum da9062_compatible_types {
|
||||
COMPAT_TYPE_DA9061 = 1,
|
||||
COMPAT_TYPE_DA9062,
|
||||
};
|
||||
|
||||
enum da9061_irqs {
|
||||
/* IRQ A */
|
||||
DA9061_IRQ_ONKEY,
|
||||
DA9061_IRQ_WDG_WARN,
|
||||
DA9061_IRQ_SEQ_RDY,
|
||||
/* IRQ B*/
|
||||
DA9061_IRQ_TEMP,
|
||||
DA9061_IRQ_LDO_LIM,
|
||||
DA9061_IRQ_DVC_RDY,
|
||||
DA9061_IRQ_VDD_WARN,
|
||||
/* IRQ C */
|
||||
DA9061_IRQ_GPI0,
|
||||
DA9061_IRQ_GPI1,
|
||||
DA9061_IRQ_GPI2,
|
||||
DA9061_IRQ_GPI3,
|
||||
DA9061_IRQ_GPI4,
|
||||
|
||||
DA9061_NUM_IRQ,
|
||||
};
|
||||
|
||||
enum da9062_irqs {
|
||||
/* IRQ A */
|
||||
DA9062_IRQ_ONKEY,
|
||||
@ -45,6 +69,7 @@ struct da9062 {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irq;
|
||||
enum da9062_compatible_types chip_type;
|
||||
};
|
||||
|
||||
#endif /* __MFD_DA9062_CORE_H__ */
|
||||
|
@ -1,6 +1,5 @@
|
||||
/*
|
||||
* registers.h - REGISTERS H for DA9062
|
||||
* Copyright (C) 2015 Dialog Semiconductor Ltd.
|
||||
* Copyright (C) 2015-2017 Dialog Semiconductor
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -18,6 +17,8 @@
|
||||
|
||||
#define DA9062_PMIC_DEVICE_ID 0x62
|
||||
#define DA9062_PMIC_VARIANT_MRC_AA 0x01
|
||||
#define DA9062_PMIC_VARIANT_VRC_DA9061 0x01
|
||||
#define DA9062_PMIC_VARIANT_VRC_DA9062 0x02
|
||||
|
||||
#define DA9062_I2C_PAGE_SEL_SHIFT 1
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* intel_bxtwc.h - Header file for Intel Broxton Whiskey Cove PMIC
|
||||
* Header file for Intel Broxton Whiskey Cove PMIC
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation. All rights reserved.
|
||||
*
|
||||
@ -13,8 +13,6 @@
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
|
||||
#ifndef __INTEL_BXTWC_H__
|
||||
#define __INTEL_BXTWC_H__
|
||||
|
@ -293,3 +293,5 @@ static inline int cpcap_get_vendor(struct device *dev,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int cpcap_sense_virq(struct regmap *regmap, int virq);
|
||||
|
187
include/linux/mfd/mxs-lradc.h
Normal file
187
include/linux/mfd/mxs-lradc.h
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Freescale MXS Low Resolution Analog-to-Digital Converter driver
|
||||
*
|
||||
* Copyright (c) 2012 DENX Software Engineering, GmbH.
|
||||
* Copyright (c) 2016 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
*
|
||||
* Author: Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 __MFD_MXS_LRADC_H
|
||||
#define __MFD_MXS_LRADC_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/stmp_device.h>
|
||||
|
||||
#define LRADC_MAX_DELAY_CHANS 4
|
||||
#define LRADC_MAX_MAPPED_CHANS 8
|
||||
#define LRADC_MAX_TOTAL_CHANS 16
|
||||
|
||||
#define LRADC_DELAY_TIMER_HZ 2000
|
||||
|
||||
#define LRADC_CTRL0 0x00
|
||||
# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23)
|
||||
# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22)
|
||||
# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21)
|
||||
# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20)
|
||||
# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19)
|
||||
# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18)
|
||||
# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17)
|
||||
# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16)
|
||||
|
||||
# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20)
|
||||
# define LRADC_CTRL0_MX23_YM BIT(19)
|
||||
# define LRADC_CTRL0_MX23_XM BIT(18)
|
||||
# define LRADC_CTRL0_MX23_YP BIT(17)
|
||||
# define LRADC_CTRL0_MX23_XP BIT(16)
|
||||
|
||||
# define LRADC_CTRL0_MX28_PLATE_MASK \
|
||||
(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
|
||||
LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
|
||||
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
|
||||
LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
|
||||
|
||||
# define LRADC_CTRL0_MX23_PLATE_MASK \
|
||||
(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
|
||||
LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
|
||||
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
|
||||
|
||||
#define LRADC_CTRL1 0x10
|
||||
#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24)
|
||||
#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
|
||||
#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16)
|
||||
#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16)
|
||||
#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
|
||||
#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8)
|
||||
#define LRADC_CTRL1_LRADC_IRQ(n) BIT(n)
|
||||
#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff
|
||||
#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff
|
||||
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
|
||||
|
||||
#define LRADC_CTRL2 0x20
|
||||
#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24
|
||||
#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15)
|
||||
|
||||
#define LRADC_STATUS 0x40
|
||||
#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0)
|
||||
|
||||
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
|
||||
#define LRADC_CH_ACCUMULATE BIT(29)
|
||||
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
|
||||
#define LRADC_CH_NUM_SAMPLES_OFFSET 24
|
||||
#define LRADC_CH_NUM_SAMPLES(x) \
|
||||
((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
|
||||
#define LRADC_CH_VALUE_MASK 0x3ffff
|
||||
#define LRADC_CH_VALUE_OFFSET 0
|
||||
|
||||
#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
|
||||
#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24)
|
||||
#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
|
||||
#define LRADC_DELAY_TRIGGER(x) \
|
||||
(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
|
||||
LRADC_DELAY_TRIGGER_LRADCS_MASK)
|
||||
#define LRADC_DELAY_KICK BIT(20)
|
||||
#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
|
||||
#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
|
||||
#define LRADC_DELAY_TRIGGER_DELAYS(x) \
|
||||
(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
|
||||
LRADC_DELAY_TRIGGER_DELAYS_MASK)
|
||||
#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
|
||||
#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
|
||||
#define LRADC_DELAY_LOOP(x) \
|
||||
(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
|
||||
LRADC_DELAY_LOOP_COUNT_MASK)
|
||||
#define LRADC_DELAY_DELAY_MASK 0x7ff
|
||||
#define LRADC_DELAY_DELAY_OFFSET 0
|
||||
#define LRADC_DELAY_DELAY(x) \
|
||||
(((x) << LRADC_DELAY_DELAY_OFFSET) & \
|
||||
LRADC_DELAY_DELAY_MASK)
|
||||
|
||||
#define LRADC_CTRL4 0x140
|
||||
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
|
||||
#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
|
||||
#define LRADC_CTRL4_LRADCSELECT(n, x) \
|
||||
(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
|
||||
LRADC_CTRL4_LRADCSELECT_MASK(n))
|
||||
|
||||
#define LRADC_RESOLUTION 12
|
||||
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
|
||||
|
||||
#define BUFFER_VCHANS_LIMITED 0x3f
|
||||
#define BUFFER_VCHANS_ALL 0xff
|
||||
|
||||
/*
|
||||
* Certain LRADC channels are shared between touchscreen
|
||||
* and/or touch-buttons and generic LRADC block. Therefore when using
|
||||
* either of these, these channels are not available for the regular
|
||||
* sampling. The shared channels are as follows:
|
||||
*
|
||||
* CH0 -- Touch button #0
|
||||
* CH1 -- Touch button #1
|
||||
* CH2 -- Touch screen XPUL
|
||||
* CH3 -- Touch screen YPLL
|
||||
* CH4 -- Touch screen XNUL
|
||||
* CH5 -- Touch screen YNLR
|
||||
* CH6 -- Touch screen WIPER (5-wire only)
|
||||
*
|
||||
* The bit fields below represents which parts of the LRADC block are
|
||||
* switched into special mode of operation. These channels can not
|
||||
* be sampled as regular LRADC channels. The driver will refuse any
|
||||
* attempt to sample these channels.
|
||||
*/
|
||||
#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0))
|
||||
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
|
||||
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
|
||||
|
||||
enum mxs_lradc_id {
|
||||
IMX23_LRADC,
|
||||
IMX28_LRADC,
|
||||
};
|
||||
|
||||
enum mxs_lradc_ts_wires {
|
||||
MXS_LRADC_TOUCHSCREEN_NONE = 0,
|
||||
MXS_LRADC_TOUCHSCREEN_4WIRE,
|
||||
MXS_LRADC_TOUCHSCREEN_5WIRE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mxs_lradc
|
||||
* @soc: soc type (IMX23 or IMX28)
|
||||
* @clk: 2 kHz clock for delay units
|
||||
* @buffer_vchans: channels that can be used during buffered capture
|
||||
* @touchscreen_wire: touchscreen type (4-wire or 5-wire)
|
||||
* @use_touchbutton: button state (on or off)
|
||||
*/
|
||||
struct mxs_lradc {
|
||||
enum mxs_lradc_id soc;
|
||||
struct clk *clk;
|
||||
u8 buffer_vchans;
|
||||
|
||||
enum mxs_lradc_ts_wires touchscreen_wire;
|
||||
bool use_touchbutton;
|
||||
};
|
||||
|
||||
static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
|
||||
{
|
||||
switch (lradc->soc) {
|
||||
case IMX23_LRADC:
|
||||
return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
|
||||
case IMX28_LRADC:
|
||||
return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __MXS_LRADC_H */
|
@ -17,157 +17,92 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define AT91SAM9_SMC_GENERIC 0x00
|
||||
#define AT91SAM9_SMC_GENERIC_BLK_SZ 0x10
|
||||
#define ATMEL_SMC_SETUP(cs) (((cs) * 0x10))
|
||||
#define ATMEL_HSMC_SETUP(cs) (0x600 + ((cs) * 0x14))
|
||||
#define ATMEL_SMC_PULSE(cs) (((cs) * 0x10) + 0x4)
|
||||
#define ATMEL_HSMC_PULSE(cs) (0x600 + ((cs) * 0x14) + 0x4)
|
||||
#define ATMEL_SMC_CYCLE(cs) (((cs) * 0x10) + 0x8)
|
||||
#define ATMEL_HSMC_CYCLE(cs) (0x600 + ((cs) * 0x14) + 0x8)
|
||||
#define ATMEL_SMC_NWE_SHIFT 0
|
||||
#define ATMEL_SMC_NCS_WR_SHIFT 8
|
||||
#define ATMEL_SMC_NRD_SHIFT 16
|
||||
#define ATMEL_SMC_NCS_RD_SHIFT 24
|
||||
|
||||
#define SAMA5_SMC_GENERIC 0x600
|
||||
#define SAMA5_SMC_GENERIC_BLK_SZ 0x14
|
||||
#define ATMEL_SMC_MODE(cs) (((cs) * 0x10) + 0xc)
|
||||
#define ATMEL_HSMC_MODE(cs) (0x600 + ((cs) * 0x14) + 0x10)
|
||||
#define ATMEL_SMC_MODE_READMODE_MASK BIT(0)
|
||||
#define ATMEL_SMC_MODE_READMODE_NCS (0 << 0)
|
||||
#define ATMEL_SMC_MODE_READMODE_NRD (1 << 0)
|
||||
#define ATMEL_SMC_MODE_WRITEMODE_MASK BIT(1)
|
||||
#define ATMEL_SMC_MODE_WRITEMODE_NCS (0 << 1)
|
||||
#define ATMEL_SMC_MODE_WRITEMODE_NWE (1 << 1)
|
||||
#define ATMEL_SMC_MODE_EXNWMODE_MASK GENMASK(5, 4)
|
||||
#define ATMEL_SMC_MODE_EXNWMODE_DISABLE (0 << 4)
|
||||
#define ATMEL_SMC_MODE_EXNWMODE_FROZEN (2 << 4)
|
||||
#define ATMEL_SMC_MODE_EXNWMODE_READY (3 << 4)
|
||||
#define ATMEL_SMC_MODE_BAT_MASK BIT(8)
|
||||
#define ATMEL_SMC_MODE_BAT_SELECT (0 << 8)
|
||||
#define ATMEL_SMC_MODE_BAT_WRITE (1 << 8)
|
||||
#define ATMEL_SMC_MODE_DBW_MASK GENMASK(13, 12)
|
||||
#define ATMEL_SMC_MODE_DBW_8 (0 << 12)
|
||||
#define ATMEL_SMC_MODE_DBW_16 (1 << 12)
|
||||
#define ATMEL_SMC_MODE_DBW_32 (2 << 12)
|
||||
#define ATMEL_SMC_MODE_TDF_MASK GENMASK(19, 16)
|
||||
#define ATMEL_SMC_MODE_TDF(x) (((x) - 1) << 16)
|
||||
#define ATMEL_SMC_MODE_TDF_MAX 16
|
||||
#define ATMEL_SMC_MODE_TDF_MIN 1
|
||||
#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED BIT(20)
|
||||
#define ATMEL_SMC_MODE_PMEN BIT(24)
|
||||
#define ATMEL_SMC_MODE_PS_MASK GENMASK(29, 28)
|
||||
#define ATMEL_SMC_MODE_PS_4 (0 << 28)
|
||||
#define ATMEL_SMC_MODE_PS_8 (1 << 28)
|
||||
#define ATMEL_SMC_MODE_PS_16 (2 << 28)
|
||||
#define ATMEL_SMC_MODE_PS_32 (3 << 28)
|
||||
|
||||
#define AT91SAM9_SMC_SETUP(o) ((o) + 0x00)
|
||||
#define AT91SAM9_SMC_NWESETUP(x) (x)
|
||||
#define AT91SAM9_SMC_NCS_WRSETUP(x) ((x) << 8)
|
||||
#define AT91SAM9_SMC_NRDSETUP(x) ((x) << 16)
|
||||
#define AT91SAM9_SMC_NCS_NRDSETUP(x) ((x) << 24)
|
||||
#define ATMEL_HSMC_TIMINGS(cs) (0x600 + ((cs) * 0x14) + 0xc)
|
||||
#define ATMEL_HSMC_TIMINGS_OCMS BIT(12)
|
||||
#define ATMEL_HSMC_TIMINGS_RBNSEL(x) ((x) << 28)
|
||||
#define ATMEL_HSMC_TIMINGS_NFSEL BIT(31)
|
||||
#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT 0
|
||||
#define ATMEL_HSMC_TIMINGS_TADL_SHIFT 4
|
||||
#define ATMEL_HSMC_TIMINGS_TAR_SHIFT 8
|
||||
#define ATMEL_HSMC_TIMINGS_TRR_SHIFT 16
|
||||
#define ATMEL_HSMC_TIMINGS_TWB_SHIFT 24
|
||||
|
||||
#define AT91SAM9_SMC_PULSE(o) ((o) + 0x04)
|
||||
#define AT91SAM9_SMC_NWEPULSE(x) (x)
|
||||
#define AT91SAM9_SMC_NCS_WRPULSE(x) ((x) << 8)
|
||||
#define AT91SAM9_SMC_NRDPULSE(x) ((x) << 16)
|
||||
#define AT91SAM9_SMC_NCS_NRDPULSE(x) ((x) << 24)
|
||||
|
||||
#define AT91SAM9_SMC_CYCLE(o) ((o) + 0x08)
|
||||
#define AT91SAM9_SMC_NWECYCLE(x) (x)
|
||||
#define AT91SAM9_SMC_NRDCYCLE(x) ((x) << 16)
|
||||
|
||||
#define AT91SAM9_SMC_MODE(o) ((o) + 0x0c)
|
||||
#define SAMA5_SMC_MODE(o) ((o) + 0x10)
|
||||
#define AT91_SMC_READMODE BIT(0)
|
||||
#define AT91_SMC_READMODE_NCS (0 << 0)
|
||||
#define AT91_SMC_READMODE_NRD (1 << 0)
|
||||
#define AT91_SMC_WRITEMODE BIT(1)
|
||||
#define AT91_SMC_WRITEMODE_NCS (0 << 1)
|
||||
#define AT91_SMC_WRITEMODE_NWE (1 << 1)
|
||||
#define AT91_SMC_EXNWMODE GENMASK(5, 4)
|
||||
#define AT91_SMC_EXNWMODE_DISABLE (0 << 4)
|
||||
#define AT91_SMC_EXNWMODE_FROZEN (2 << 4)
|
||||
#define AT91_SMC_EXNWMODE_READY (3 << 4)
|
||||
#define AT91_SMC_BAT BIT(8)
|
||||
#define AT91_SMC_BAT_SELECT (0 << 8)
|
||||
#define AT91_SMC_BAT_WRITE (1 << 8)
|
||||
#define AT91_SMC_DBW GENMASK(13, 12)
|
||||
#define AT91_SMC_DBW_8 (0 << 12)
|
||||
#define AT91_SMC_DBW_16 (1 << 12)
|
||||
#define AT91_SMC_DBW_32 (2 << 12)
|
||||
#define AT91_SMC_TDF GENMASK(19, 16)
|
||||
#define AT91_SMC_TDF_(x) ((((x) - 1) << 16) & AT91_SMC_TDF)
|
||||
#define AT91_SMC_TDF_MAX 16
|
||||
#define AT91_SMC_TDFMODE_OPTIMIZED BIT(20)
|
||||
#define AT91_SMC_PMEN BIT(24)
|
||||
#define AT91_SMC_PS GENMASK(29, 28)
|
||||
#define AT91_SMC_PS_4 (0 << 28)
|
||||
#define AT91_SMC_PS_8 (1 << 28)
|
||||
#define AT91_SMC_PS_16 (2 << 28)
|
||||
#define AT91_SMC_PS_32 (3 << 28)
|
||||
|
||||
|
||||
/*
|
||||
* This function converts a setup timing expressed in nanoseconds into an
|
||||
* encoded value that can be written in the SMC_SETUP register.
|
||||
*
|
||||
* The following formula is described in atmel datasheets (section
|
||||
* "SMC Setup Register"):
|
||||
*
|
||||
* setup length = (128* SETUP[5] + SETUP[4:0])
|
||||
*
|
||||
* where setup length is the timing expressed in cycles.
|
||||
/**
|
||||
* struct atmel_smc_cs_conf - SMC CS config as described in the datasheet.
|
||||
* @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200)
|
||||
* @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200)
|
||||
* @cycle: NWE/NRD cycle timings (not applicable to at91rm9200)
|
||||
* @timings: advanced NAND related timings (only applicable to HSMC)
|
||||
* @mode: all kind of config parameters (see the fields definition above).
|
||||
* The mode fields are different on at91rm9200
|
||||
*/
|
||||
static inline u32 at91sam9_smc_setup_ns_to_cycles(unsigned int clk_rate,
|
||||
u32 timing_ns)
|
||||
{
|
||||
u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate);
|
||||
u32 coded_cycles = 0;
|
||||
u32 cycles;
|
||||
struct atmel_smc_cs_conf {
|
||||
u32 setup;
|
||||
u32 pulse;
|
||||
u32 cycle;
|
||||
u32 timings;
|
||||
u32 mode;
|
||||
};
|
||||
|
||||
cycles = DIV_ROUND_UP(timing_ns, clk_period);
|
||||
if (cycles / 32) {
|
||||
coded_cycles |= 1 << 5;
|
||||
if (cycles < 128)
|
||||
cycles = 0;
|
||||
}
|
||||
|
||||
coded_cycles |= cycles % 32;
|
||||
|
||||
return coded_cycles;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function converts a pulse timing expressed in nanoseconds into an
|
||||
* encoded value that can be written in the SMC_PULSE register.
|
||||
*
|
||||
* The following formula is described in atmel datasheets (section
|
||||
* "SMC Pulse Register"):
|
||||
*
|
||||
* pulse length = (256* PULSE[6] + PULSE[5:0])
|
||||
*
|
||||
* where pulse length is the timing expressed in cycles.
|
||||
*/
|
||||
static inline u32 at91sam9_smc_pulse_ns_to_cycles(unsigned int clk_rate,
|
||||
u32 timing_ns)
|
||||
{
|
||||
u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate);
|
||||
u32 coded_cycles = 0;
|
||||
u32 cycles;
|
||||
|
||||
cycles = DIV_ROUND_UP(timing_ns, clk_period);
|
||||
if (cycles / 64) {
|
||||
coded_cycles |= 1 << 6;
|
||||
if (cycles < 256)
|
||||
cycles = 0;
|
||||
}
|
||||
|
||||
coded_cycles |= cycles % 64;
|
||||
|
||||
return coded_cycles;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function converts a cycle timing expressed in nanoseconds into an
|
||||
* encoded value that can be written in the SMC_CYCLE register.
|
||||
*
|
||||
* The following formula is described in atmel datasheets (section
|
||||
* "SMC Cycle Register"):
|
||||
*
|
||||
* cycle length = (CYCLE[8:7]*256 + CYCLE[6:0])
|
||||
*
|
||||
* where cycle length is the timing expressed in cycles.
|
||||
*/
|
||||
static inline u32 at91sam9_smc_cycle_ns_to_cycles(unsigned int clk_rate,
|
||||
u32 timing_ns)
|
||||
{
|
||||
u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate);
|
||||
u32 coded_cycles = 0;
|
||||
u32 cycles;
|
||||
|
||||
cycles = DIV_ROUND_UP(timing_ns, clk_period);
|
||||
if (cycles / 128) {
|
||||
coded_cycles = cycles / 256;
|
||||
cycles %= 256;
|
||||
if (cycles >= 128) {
|
||||
coded_cycles++;
|
||||
cycles = 0;
|
||||
}
|
||||
|
||||
if (coded_cycles > 0x3) {
|
||||
coded_cycles = 0x3;
|
||||
cycles = 0x7f;
|
||||
}
|
||||
|
||||
coded_cycles <<= 7;
|
||||
}
|
||||
|
||||
coded_cycles |= cycles % 128;
|
||||
|
||||
return coded_cycles;
|
||||
}
|
||||
void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf);
|
||||
int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift,
|
||||
unsigned int ncycles);
|
||||
int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles);
|
||||
int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles);
|
||||
int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
|
||||
unsigned int shift, unsigned int ncycles);
|
||||
void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
|
||||
const struct atmel_smc_cs_conf *conf);
|
||||
void atmel_hsmc_cs_conf_apply(struct regmap *regmap, int cs,
|
||||
const struct atmel_smc_cs_conf *conf);
|
||||
void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
|
||||
struct atmel_smc_cs_conf *conf);
|
||||
void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs,
|
||||
struct atmel_smc_cs_conf *conf);
|
||||
|
||||
#endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */
|
||||
|
@ -46,7 +46,4 @@
|
||||
#define EXYNOS5_MIPI_PHY_S_RESETN BIT(1)
|
||||
#define EXYNOS5_MIPI_PHY_M_RESETN BIT(2)
|
||||
|
||||
#define EXYNOS5433_PAD_RETENTION_AUD_OPTION (0x3028)
|
||||
#define EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR BIT(28)
|
||||
|
||||
#endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ */
|
||||
|
280
include/linux/mfd/ti-lmu-register.h
Normal file
280
include/linux/mfd/ti-lmu-register.h
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* TI LMU (Lighting Management Unit) Device Register Map
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MFD_TI_LMU_REGISTER_H__
|
||||
#define __MFD_TI_LMU_REGISTER_H__
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* LM3532 */
|
||||
#define LM3532_REG_OUTPUT_CFG 0x10
|
||||
#define LM3532_ILED1_CFG_MASK 0x03
|
||||
#define LM3532_ILED2_CFG_MASK 0x0C
|
||||
#define LM3532_ILED3_CFG_MASK 0x30
|
||||
#define LM3532_ILED1_CFG_SHIFT 0
|
||||
#define LM3532_ILED2_CFG_SHIFT 2
|
||||
#define LM3532_ILED3_CFG_SHIFT 4
|
||||
|
||||
#define LM3532_REG_RAMPUP 0x12
|
||||
#define LM3532_REG_RAMPDN LM3532_REG_RAMPUP
|
||||
#define LM3532_RAMPUP_MASK 0x07
|
||||
#define LM3532_RAMPUP_SHIFT 0
|
||||
#define LM3532_RAMPDN_MASK 0x38
|
||||
#define LM3532_RAMPDN_SHIFT 3
|
||||
|
||||
#define LM3532_REG_ENABLE 0x1D
|
||||
|
||||
#define LM3532_REG_PWM_A_CFG 0x13
|
||||
#define LM3532_PWM_A_MASK 0x05 /* zone 0 */
|
||||
#define LM3532_PWM_ZONE_0 BIT(2)
|
||||
|
||||
#define LM3532_REG_PWM_B_CFG 0x14
|
||||
#define LM3532_PWM_B_MASK 0x09 /* zone 1 */
|
||||
#define LM3532_PWM_ZONE_1 BIT(3)
|
||||
|
||||
#define LM3532_REG_PWM_C_CFG 0x15
|
||||
#define LM3532_PWM_C_MASK 0x11 /* zone 2 */
|
||||
#define LM3532_PWM_ZONE_2 BIT(4)
|
||||
|
||||
#define LM3532_REG_ZONE_CFG_A 0x16
|
||||
#define LM3532_REG_ZONE_CFG_B 0x18
|
||||
#define LM3532_REG_ZONE_CFG_C 0x1A
|
||||
#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4))
|
||||
#define LM3532_ZONE_0 0
|
||||
#define LM3532_ZONE_1 BIT(2)
|
||||
#define LM3532_ZONE_2 BIT(3)
|
||||
|
||||
#define LM3532_REG_BRT_A 0x70 /* zone 0 */
|
||||
#define LM3532_REG_BRT_B 0x76 /* zone 1 */
|
||||
#define LM3532_REG_BRT_C 0x7C /* zone 2 */
|
||||
|
||||
#define LM3532_MAX_REG 0x7E
|
||||
|
||||
/* LM3631 */
|
||||
#define LM3631_REG_DEVCTRL 0x00
|
||||
#define LM3631_LCD_EN_MASK BIT(1)
|
||||
#define LM3631_BL_EN_MASK BIT(0)
|
||||
|
||||
#define LM3631_REG_BRT_LSB 0x01
|
||||
#define LM3631_REG_BRT_MSB 0x02
|
||||
|
||||
#define LM3631_REG_BL_CFG 0x06
|
||||
#define LM3631_BL_CHANNEL_MASK BIT(3)
|
||||
#define LM3631_BL_DUAL_CHANNEL 0
|
||||
#define LM3631_BL_SINGLE_CHANNEL BIT(3)
|
||||
#define LM3631_MAP_MASK BIT(5)
|
||||
#define LM3631_EXPONENTIAL_MAP 0
|
||||
|
||||
#define LM3631_REG_BRT_MODE 0x08
|
||||
#define LM3631_MODE_MASK (BIT(1) | BIT(2) | BIT(3))
|
||||
#define LM3631_DEFAULT_MODE (BIT(1) | BIT(3))
|
||||
|
||||
#define LM3631_REG_SLOPE 0x09
|
||||
#define LM3631_SLOPE_MASK 0xF0
|
||||
#define LM3631_SLOPE_SHIFT 4
|
||||
|
||||
#define LM3631_REG_LDO_CTRL1 0x0A
|
||||
#define LM3631_EN_OREF_MASK BIT(0)
|
||||
#define LM3631_EN_VNEG_MASK BIT(1)
|
||||
#define LM3631_EN_VPOS_MASK BIT(2)
|
||||
|
||||
#define LM3631_REG_LDO_CTRL2 0x0B
|
||||
#define LM3631_EN_CONT_MASK BIT(0)
|
||||
|
||||
#define LM3631_REG_VOUT_CONT 0x0C
|
||||
#define LM3631_VOUT_CONT_MASK (BIT(6) | BIT(7))
|
||||
|
||||
#define LM3631_REG_VOUT_BOOST 0x0C
|
||||
#define LM3631_REG_VOUT_POS 0x0D
|
||||
#define LM3631_REG_VOUT_NEG 0x0E
|
||||
#define LM3631_REG_VOUT_OREF 0x0F
|
||||
#define LM3631_VOUT_MASK 0x3F
|
||||
|
||||
#define LM3631_REG_ENTIME_VCONT 0x0B
|
||||
#define LM3631_ENTIME_CONT_MASK 0x70
|
||||
|
||||
#define LM3631_REG_ENTIME_VOREF 0x0F
|
||||
#define LM3631_REG_ENTIME_VPOS 0x10
|
||||
#define LM3631_REG_ENTIME_VNEG 0x11
|
||||
#define LM3631_ENTIME_MASK 0xF0
|
||||
#define LM3631_ENTIME_SHIFT 4
|
||||
|
||||
#define LM3631_MAX_REG 0x16
|
||||
|
||||
/* LM3632 */
|
||||
#define LM3632_REG_CONFIG1 0x02
|
||||
#define LM3632_OVP_MASK (BIT(5) | BIT(6) | BIT(7))
|
||||
#define LM3632_OVP_25V BIT(6)
|
||||
|
||||
#define LM3632_REG_CONFIG2 0x03
|
||||
#define LM3632_SWFREQ_MASK BIT(7)
|
||||
#define LM3632_SWFREQ_1MHZ BIT(7)
|
||||
|
||||
#define LM3632_REG_BRT_LSB 0x04
|
||||
#define LM3632_REG_BRT_MSB 0x05
|
||||
|
||||
#define LM3632_REG_IO_CTRL 0x09
|
||||
#define LM3632_PWM_MASK BIT(6)
|
||||
#define LM3632_I2C_MODE 0
|
||||
#define LM3632_PWM_MODE BIT(6)
|
||||
|
||||
#define LM3632_REG_ENABLE 0x0A
|
||||
#define LM3632_BL_EN_MASK BIT(0)
|
||||
#define LM3632_BL_CHANNEL_MASK (BIT(3) | BIT(4))
|
||||
#define LM3632_BL_SINGLE_CHANNEL BIT(4)
|
||||
#define LM3632_BL_DUAL_CHANNEL BIT(3)
|
||||
|
||||
#define LM3632_REG_BIAS_CONFIG 0x0C
|
||||
#define LM3632_EXT_EN_MASK BIT(0)
|
||||
#define LM3632_EN_VNEG_MASK BIT(1)
|
||||
#define LM3632_EN_VPOS_MASK BIT(2)
|
||||
|
||||
#define LM3632_REG_VOUT_BOOST 0x0D
|
||||
#define LM3632_REG_VOUT_POS 0x0E
|
||||
#define LM3632_REG_VOUT_NEG 0x0F
|
||||
#define LM3632_VOUT_MASK 0x3F
|
||||
|
||||
#define LM3632_MAX_REG 0x10
|
||||
|
||||
/* LM3633 */
|
||||
#define LM3633_REG_HVLED_OUTPUT_CFG 0x10
|
||||
#define LM3633_HVLED1_CFG_MASK BIT(0)
|
||||
#define LM3633_HVLED2_CFG_MASK BIT(1)
|
||||
#define LM3633_HVLED3_CFG_MASK BIT(2)
|
||||
#define LM3633_HVLED1_CFG_SHIFT 0
|
||||
#define LM3633_HVLED2_CFG_SHIFT 1
|
||||
#define LM3633_HVLED3_CFG_SHIFT 2
|
||||
|
||||
#define LM3633_REG_BANK_SEL 0x11
|
||||
|
||||
#define LM3633_REG_BL0_RAMP 0x12
|
||||
#define LM3633_REG_BL1_RAMP 0x13
|
||||
#define LM3633_BL_RAMPUP_MASK 0xF0
|
||||
#define LM3633_BL_RAMPUP_SHIFT 4
|
||||
#define LM3633_BL_RAMPDN_MASK 0x0F
|
||||
#define LM3633_BL_RAMPDN_SHIFT 0
|
||||
|
||||
#define LM3633_REG_BL_RAMP_CONF 0x1B
|
||||
#define LM3633_BL_RAMP_MASK 0x0F
|
||||
#define LM3633_BL_RAMP_EACH 0x05
|
||||
|
||||
#define LM3633_REG_PTN0_RAMP 0x1C
|
||||
#define LM3633_REG_PTN1_RAMP 0x1D
|
||||
#define LM3633_PTN_RAMPUP_MASK 0x70
|
||||
#define LM3633_PTN_RAMPUP_SHIFT 4
|
||||
#define LM3633_PTN_RAMPDN_MASK 0x07
|
||||
#define LM3633_PTN_RAMPDN_SHIFT 0
|
||||
|
||||
#define LM3633_REG_LED_MAPPING_MODE 0x1F
|
||||
#define LM3633_LED_EXPONENTIAL BIT(1)
|
||||
|
||||
#define LM3633_REG_IMAX_HVLED_A 0x20
|
||||
#define LM3633_REG_IMAX_HVLED_B 0x21
|
||||
#define LM3633_REG_IMAX_LVLED_BASE 0x22
|
||||
|
||||
#define LM3633_REG_BL_FEEDBACK_ENABLE 0x28
|
||||
|
||||
#define LM3633_REG_ENABLE 0x2B
|
||||
#define LM3633_LED_BANK_OFFSET 2
|
||||
|
||||
#define LM3633_REG_PATTERN 0x2C
|
||||
|
||||
#define LM3633_REG_BOOST_CFG 0x2D
|
||||
#define LM3633_OVP_MASK (BIT(1) | BIT(2))
|
||||
#define LM3633_OVP_40V 0x6
|
||||
|
||||
#define LM3633_REG_PWM_CFG 0x2F
|
||||
#define LM3633_PWM_A_MASK BIT(0)
|
||||
#define LM3633_PWM_B_MASK BIT(1)
|
||||
|
||||
#define LM3633_REG_BRT_HVLED_A_LSB 0x40
|
||||
#define LM3633_REG_BRT_HVLED_A_MSB 0x41
|
||||
#define LM3633_REG_BRT_HVLED_B_LSB 0x42
|
||||
#define LM3633_REG_BRT_HVLED_B_MSB 0x43
|
||||
|
||||
#define LM3633_REG_BRT_LVLED_BASE 0x44
|
||||
|
||||
#define LM3633_REG_PTN_DELAY 0x50
|
||||
|
||||
#define LM3633_REG_PTN_LOWTIME 0x51
|
||||
|
||||
#define LM3633_REG_PTN_HIGHTIME 0x52
|
||||
|
||||
#define LM3633_REG_PTN_LOWBRT 0x53
|
||||
|
||||
#define LM3633_REG_PTN_HIGHBRT LM3633_REG_BRT_LVLED_BASE
|
||||
|
||||
#define LM3633_REG_BL_OPEN_FAULT_STATUS 0xB0
|
||||
|
||||
#define LM3633_REG_BL_SHORT_FAULT_STATUS 0xB2
|
||||
|
||||
#define LM3633_REG_MONITOR_ENABLE 0xB4
|
||||
|
||||
#define LM3633_MAX_REG 0xB4
|
||||
|
||||
/* LM3695 */
|
||||
#define LM3695_REG_GP 0x10
|
||||
#define LM3695_BL_CHANNEL_MASK BIT(3)
|
||||
#define LM3695_BL_DUAL_CHANNEL 0
|
||||
#define LM3695_BL_SINGLE_CHANNEL BIT(3)
|
||||
#define LM3695_BRT_RW_MASK BIT(2)
|
||||
#define LM3695_BL_EN_MASK BIT(0)
|
||||
|
||||
#define LM3695_REG_BRT_LSB 0x13
|
||||
#define LM3695_REG_BRT_MSB 0x14
|
||||
|
||||
#define LM3695_MAX_REG 0x14
|
||||
|
||||
/* LM3697 */
|
||||
#define LM3697_REG_HVLED_OUTPUT_CFG 0x10
|
||||
#define LM3697_HVLED1_CFG_MASK BIT(0)
|
||||
#define LM3697_HVLED2_CFG_MASK BIT(1)
|
||||
#define LM3697_HVLED3_CFG_MASK BIT(2)
|
||||
#define LM3697_HVLED1_CFG_SHIFT 0
|
||||
#define LM3697_HVLED2_CFG_SHIFT 1
|
||||
#define LM3697_HVLED3_CFG_SHIFT 2
|
||||
|
||||
#define LM3697_REG_BL0_RAMP 0x11
|
||||
#define LM3697_REG_BL1_RAMP 0x12
|
||||
#define LM3697_RAMPUP_MASK 0xF0
|
||||
#define LM3697_RAMPUP_SHIFT 4
|
||||
#define LM3697_RAMPDN_MASK 0x0F
|
||||
#define LM3697_RAMPDN_SHIFT 0
|
||||
|
||||
#define LM3697_REG_RAMP_CONF 0x14
|
||||
#define LM3697_RAMP_MASK 0x0F
|
||||
#define LM3697_RAMP_EACH 0x05
|
||||
|
||||
#define LM3697_REG_PWM_CFG 0x1C
|
||||
#define LM3697_PWM_A_MASK BIT(0)
|
||||
#define LM3697_PWM_B_MASK BIT(1)
|
||||
|
||||
#define LM3697_REG_IMAX_A 0x17
|
||||
#define LM3697_REG_IMAX_B 0x18
|
||||
|
||||
#define LM3697_REG_FEEDBACK_ENABLE 0x19
|
||||
|
||||
#define LM3697_REG_BRT_A_LSB 0x20
|
||||
#define LM3697_REG_BRT_A_MSB 0x21
|
||||
#define LM3697_REG_BRT_B_LSB 0x22
|
||||
#define LM3697_REG_BRT_B_MSB 0x23
|
||||
|
||||
#define LM3697_REG_ENABLE 0x24
|
||||
|
||||
#define LM3697_REG_OPEN_FAULT_STATUS 0xB0
|
||||
|
||||
#define LM3697_REG_SHORT_FAULT_STATUS 0xB2
|
||||
|
||||
#define LM3697_REG_MONITOR_ENABLE 0xB4
|
||||
|
||||
#define LM3697_MAX_REG 0xB4
|
||||
#endif
|
87
include/linux/mfd/ti-lmu.h
Normal file
87
include/linux/mfd/ti-lmu.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* TI LMU (Lighting Management Unit) Devices
|
||||
*
|
||||
* Copyright 2017 Texas Instruments
|
||||
*
|
||||
* Author: Milo Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MFD_TI_LMU_H__
|
||||
#define __MFD_TI_LMU_H__
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Notifier event */
|
||||
#define LMU_EVENT_MONITOR_DONE 0x01
|
||||
|
||||
enum ti_lmu_id {
|
||||
LM3532,
|
||||
LM3631,
|
||||
LM3632,
|
||||
LM3633,
|
||||
LM3695,
|
||||
LM3697,
|
||||
LMU_MAX_ID,
|
||||
};
|
||||
|
||||
enum ti_lmu_max_current {
|
||||
LMU_IMAX_5mA,
|
||||
LMU_IMAX_6mA,
|
||||
LMU_IMAX_7mA = 0x03,
|
||||
LMU_IMAX_8mA,
|
||||
LMU_IMAX_9mA,
|
||||
LMU_IMAX_10mA = 0x07,
|
||||
LMU_IMAX_11mA,
|
||||
LMU_IMAX_12mA,
|
||||
LMU_IMAX_13mA,
|
||||
LMU_IMAX_14mA,
|
||||
LMU_IMAX_15mA = 0x0D,
|
||||
LMU_IMAX_16mA,
|
||||
LMU_IMAX_17mA,
|
||||
LMU_IMAX_18mA,
|
||||
LMU_IMAX_19mA,
|
||||
LMU_IMAX_20mA = 0x13,
|
||||
LMU_IMAX_21mA,
|
||||
LMU_IMAX_22mA,
|
||||
LMU_IMAX_23mA = 0x17,
|
||||
LMU_IMAX_24mA,
|
||||
LMU_IMAX_25mA,
|
||||
LMU_IMAX_26mA,
|
||||
LMU_IMAX_27mA = 0x1C,
|
||||
LMU_IMAX_28mA,
|
||||
LMU_IMAX_29mA,
|
||||
LMU_IMAX_30mA,
|
||||
};
|
||||
|
||||
enum lm363x_regulator_id {
|
||||
LM3631_BOOST, /* Boost output */
|
||||
LM3631_LDO_CONT, /* Display panel controller */
|
||||
LM3631_LDO_OREF, /* Gamma reference */
|
||||
LM3631_LDO_POS, /* Positive display bias output */
|
||||
LM3631_LDO_NEG, /* Negative display bias output */
|
||||
LM3632_BOOST, /* Boost output */
|
||||
LM3632_LDO_POS, /* Positive display bias output */
|
||||
LM3632_LDO_NEG, /* Negative display bias output */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_lmu
|
||||
*
|
||||
* @dev: Parent device pointer
|
||||
* @regmap: Used for i2c communcation on accessing registers
|
||||
* @en_gpio: GPIO for HWEN pin [Optional]
|
||||
* @notifier: Notifier for reporting hwmon event
|
||||
*/
|
||||
struct ti_lmu {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
int en_gpio;
|
||||
struct blocking_notifier_head notifier;
|
||||
};
|
||||
#endif
|
@ -21,6 +21,8 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/wm831x/auxadc.h>
|
||||
#include <linux/mfd/wm831x/pdata.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* Register values.
|
||||
@ -367,6 +369,9 @@ struct wm831x {
|
||||
|
||||
struct regmap *regmap;
|
||||
|
||||
struct wm831x_pdata pdata;
|
||||
enum wm831x_parent type;
|
||||
|
||||
int irq; /* Our chip IRQ */
|
||||
struct mutex irq_lock;
|
||||
struct irq_domain *irq_domain;
|
||||
@ -412,7 +417,7 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
|
||||
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
|
||||
int count, u16 *buf);
|
||||
|
||||
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq);
|
||||
int wm831x_device_init(struct wm831x *wm831x, int irq);
|
||||
void wm831x_device_exit(struct wm831x *wm831x);
|
||||
int wm831x_device_suspend(struct wm831x *wm831x);
|
||||
void wm831x_device_shutdown(struct wm831x *wm831x);
|
||||
@ -427,4 +432,6 @@ static inline int wm831x_irq(struct wm831x *wm831x, int irq)
|
||||
|
||||
extern struct regmap_config wm831x_regmap_config;
|
||||
|
||||
extern const struct of_device_id wm831x_of_match[];
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user