== Changes to existing drivers ==
- Constify structures; throughout the subsystem - Move support to DT in; cros_ec - DT changes and documentation; cros-ec, max77693, max77686, arizona, da9063 - ACPI changes and documentation; mfd-core - Use different platform specific API in; cros_ec_*, arizona-core - Remove unused parent field from; cros_ec_i2c - Add wake-up/reset delay in; cross_ec_spi, arizona-core - Staticise structures/functions in; cros_ec - Remove redundant code; arizona-core, max77686 - Bugfix; twl4030-power - Allow compile test; aat2870, tps65910 - MAINTAINERS adaptions; samsung, syscon - Resource Management (devm_*); arizona-core - Refactor Reset code; arizona-core - Insist on at least one full boot; arizona-core - Trivial formatting; arizona-core - Add low-power-sleep; arizona-core - IRQ ONESHOT changes; twl4030-irq, mc13xxx-core, wm831x-auxadc, htc-i2cpld, wm8350-core, ab8500-debugfs, ab8500-gpadc, si476x-i2c == (Re-)moved drivers == - Move protocol helpers out to drivers/platform; cros_ec == New drivers/supported devices == - Add support for AXP22x into axp20x - Add support for OnKey into da9063-core - Add support for Pinctrl into mt6397-core - New STMicroelectronics LPC Watchdog driver - New STMicroelectronics LPC Real-Time Clock driver -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJViWsQAAoJEFGvii+H/HdhBSEP/1nBi6iCwAb0gGJ5HeVKAVGa bF9EYM3wMEu64Rb3b4CZGVaI2DWy148vsyDdoOht6gRpaGk9yWz4KR9saG0cemKe NC1fqOrY+a6FytqJOcQ51fhcXAn49uqAOzJsTZX3AQ4Z93EXT3ZhF8/shXqUXNc1 rXzV5enMwco9xnc3+0qzJoA0RaFfZuLB33bxt53GmnJVdnc5b1Haj5t40IN2oDSd 3pA2MQqVw/j4rGwsYOoTkJHK792X969BJHj9AHESwFYz87/u2f8RQin5xi3RWN/M 1XtkvURZESA+ewPWbOsq6wiVZd/wm4i2knoqWeXx0S5uKpi48PaljgY/PNYt8cOt oC1kVA5oOFIksdue7HG+mJ1EdAd38m3OGDJrivfFCfn8O8U+wsVrmrpS/hPuWQr6 JTsZKapS77vty+jDSHrCU/F1rd5M7fVucxHqum0YCHz6w+B5CYWY3+qLcVa5zimi f3LbcuWI5XsNzuAsW3iqF1M+bPT6G9GRJV30FczX7KQkLNT0++q62lMF4K3mz93m avtrmZxFrF0yAP1n4Molz5x8JgfWdyvCSuGGxxfYmGf2v4taLW2BSRFRrWE3WwN3 KQDzcftt3R7CM50wkIVqKJwBP7pA/UV+PPaRopbPqsdeXnkL+EoYyOpDuQkKsblz Q5M3ChB5rsu8trNyMPI6 =Y02f -----END PGP SIGNATURE----- Merge tag 'mfd-for-linus-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd Pull MFD updates from Lee Jones: "Changes to existing drivers: - Constify structures; throughout the subsystem - Move support to DT in; cros_ec - DT changes and documentation; cros-ec, max77693, max77686, arizona, da9063 - ACPI changes and documentation; mfd-core - Use different platform specific API in; cros_ec_*, arizona-core - Remove unused parent field from; cros_ec_i2c - Add wake-up/reset delay in; cross_ec_spi, arizona-core - Staticise structures/functions in; cros_ec - Remove redundant code; arizona-core, max77686 - Bugfix; twl4030-power - Allow compile test; aat2870, tps65910 - MAINTAINERS adaptions; samsung, syscon - Resource Management (devm_*); arizona-core - Refactor Reset code; arizona-core - Insist on at least one full boot; arizona-core - Trivial formatting; arizona-core - Add low-power-sleep; arizona-core - IRQ ONESHOT changes; twl4030-irq, mc13xxx-core, wm831x-auxadc, htc-i2cpld, wm8350-core, ab8500-debugfs, ab8500-gpadc, si476x-i2c (Re-)moved drivers: - Move protocol helpers out to drivers/platform; cros_ec New drivers/supported devices: - Add support for AXP22x into axp20x - Add support for OnKey into da9063-core - Add support for Pinctrl into mt6397-core - New STMicroelectronics LPC Watchdog driver - New STMicroelectronics LPC Real-Time Clock driver" * tag 'mfd-for-linus-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (59 commits) mfd: lpc_ich: Assign subdevice ids automatically mfd: si476x-i2c: Pass the IRQF_ONESHOT flag mfd: ab8500-gpadc: Pass the IRQF_ONESHOT flag mfd: ab8500-debugfs: Pass the IRQF_ONESHOT flag mfd: wm8350-core: Pass the IRQF_ONESHOT flag mfd: htc-i2cpld: Pass the IRQF_ONESHOT flag mfd: wm831x-auxadc: Pass the IRQF_ONESHOT flag mfd: mc13xxx-core: Pass the IRQF_ONESHOT flag mfd: twl4030-irq: Pass the IRQF_ONESHOT flag mfd: mt6397-core: Add GPIO sub-module support mfd: arizona: Add convience defines for micd_rate/micd_bias_starttime mfd: dt: Add bindings for DA9063 OnKey mfd: da9063: Add support for OnKey driver mfd: arizona: Fix incorrect Makefile conditionals mfd: arizona: Add stub for wm5102_patch() mfd: Check ACPI device companion before checking resources Documentation: Add WM8998/WM1814 device tree bindings mfd: arizona: Split INx_MODE into two fields mfd: wm5110: Add delay before releasing reset line mfd: arizona: Add better support for system suspend ...
This commit is contained in:
commit
84e9c87e6f
|
@ -10,6 +10,9 @@ Required properties:
|
|||
"wlf,wm5110"
|
||||
"wlf,wm8280"
|
||||
"wlf,wm8997"
|
||||
"wlf,wm8998"
|
||||
"wlf,wm1814"
|
||||
|
||||
- reg : I2C slave address when connected using I2C, chip select number when
|
||||
using SPI.
|
||||
|
||||
|
@ -31,10 +34,10 @@ Required properties:
|
|||
as covered in Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
- DBVDD2-supply, DBVDD3-supply : Additional databus power supplies (wm5102,
|
||||
wm5110, wm8280)
|
||||
wm5110, wm8280, wm8998, wm1814)
|
||||
|
||||
- SPKVDDL-supply, SPKVDDR-supply : Speaker driver power supplies (wm5102,
|
||||
wm5110, wm8280)
|
||||
wm5110, wm8280, wm8998, wm1814)
|
||||
|
||||
- SPKVDD-supply : Speaker driver power supply (wm8997)
|
||||
|
||||
|
@ -53,8 +56,10 @@ Optional properties:
|
|||
of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
|
||||
2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
|
||||
If present, values must be specified less than or equal to the number of
|
||||
input singals. If values less than the number of input signals, elements
|
||||
that has not been specifed are set to 0 by default.
|
||||
input signals. If values less than the number of input signals, elements
|
||||
that have not been specified are set to 0 by default. Entries are:
|
||||
<IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997)
|
||||
<IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814)
|
||||
|
||||
- wlf,dmic-ref : DMIC reference voltage source for each input, can be
|
||||
selected from either MICVDD or one of the MICBIAS's, defines
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
AXP202/AXP209 device tree bindings
|
||||
AXP family PMIC device tree bindings
|
||||
|
||||
The axp20x family current members :
|
||||
axp202 (X-Powers)
|
||||
axp209 (X-Powers)
|
||||
axp221 (X-Powers)
|
||||
|
||||
Required properties:
|
||||
- compatible: "x-powers,axp202" or "x-powers,axp209"
|
||||
- compatible: "x-powers,axp202", "x-powers,axp209", "x-powers,axp221"
|
||||
- reg: The I2C slave address for the AXP chip
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
|
||||
- interrupt-controller: axp20x has its own internal IRQs
|
||||
- interrupt-controller: The PMIC has its own internal IRQs
|
||||
- #interrupt-cells: Should be set to 1
|
||||
|
||||
Optional properties:
|
||||
|
@ -48,6 +49,31 @@ LDO3 : LDO : ldo3in-supply
|
|||
LDO4 : LDO : ldo24in-supply : shared supply
|
||||
LDO5 : LDO : ldo5in-supply
|
||||
|
||||
AXP221 regulators, type, and corresponding input supply names:
|
||||
|
||||
Regulator Type Supply Name Notes
|
||||
--------- ---- ----------- -----
|
||||
DCDC1 : DC-DC buck : vin1-supply
|
||||
DCDC2 : DC-DC buck : vin2-supply
|
||||
DCDC3 : DC-DC buck : vin3-supply
|
||||
DCDC4 : DC-DC buck : vin4-supply
|
||||
DCDC5 : DC-DC buck : vin5-supply
|
||||
DC1SW : On/Off Switch : dcdc1-supply : DCDC1 secondary output
|
||||
DC5LDO : LDO : dcdc5-supply : input from DCDC5
|
||||
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
|
||||
LDO_IO0 : LDO : ips-supply : GPIO 0
|
||||
LDO_IO1 : LDO : ips-supply : GPIO 1
|
||||
RTC_LDO : LDO : ips-supply : always on
|
||||
|
||||
Example:
|
||||
|
||||
axp209: pmic@34 {
|
||||
|
|
|
@ -18,6 +18,10 @@ Required properties (SPI):
|
|||
- reg: SPI chip select
|
||||
|
||||
Optional properties (SPI):
|
||||
- google,cros-ec-spi-pre-delay: Some implementations of the EC need a little
|
||||
time to wake up from sleep before they can receive SPI transfers at a high
|
||||
clock rate. This property specifies the delay, in usecs, between the
|
||||
assertion of the CS to the start of the first clock pulse.
|
||||
- google,cros-ec-spi-msg-delay: Some implementations of the EC require some
|
||||
additional processing time in order to accept new transactions. If the delay
|
||||
between transactions is not long enough the EC may not be able to respond
|
||||
|
|
|
@ -5,6 +5,7 @@ DA9093 consists of a large and varied group of sub-devices (I2C Only):
|
|||
Device Supply Names Description
|
||||
------ ------------ -----------
|
||||
da9063-regulator : : LDOs & BUCKs
|
||||
da9063-onkey : : On Key
|
||||
da9063-rtc : : Real-Time Clock
|
||||
da9063-watchdog : : Watchdog
|
||||
|
||||
|
@ -51,6 +52,18 @@ Sub-nodes:
|
|||
the DA9063. There are currently no entries in this binding, however
|
||||
compatible = "dlg,da9063-rtc" should be added if a node is created.
|
||||
|
||||
- onkey : This node defines the OnKey settings for controlling the key
|
||||
functionality of the device. The node should contain the compatible property
|
||||
with the value "dlg,da9063-onkey".
|
||||
|
||||
Optional onkey properties:
|
||||
|
||||
- dlg,disable-key-power : Disable power-down using a long key-press. If this
|
||||
entry exists the OnKey driver will remove support for the KEY_POWER key
|
||||
press. If this entry does not exist then by default the key-press
|
||||
triggered power down is enabled and the OnKey will support both KEY_POWER
|
||||
and KEY_SLEEP.
|
||||
|
||||
- watchdog : This node defines settings for the Watchdog timer associated
|
||||
with the DA9063. There are currently no entries in this binding, however
|
||||
compatible = "dlg,da9063-watchdog" should be added if a node is created.
|
||||
|
@ -73,6 +86,11 @@ Example:
|
|||
compatible = "dlg,da9063-watchdog";
|
||||
};
|
||||
|
||||
onkey {
|
||||
compatible = "dlg,da9063-onkey";
|
||||
dlg,disable-key-power;
|
||||
};
|
||||
|
||||
regulators {
|
||||
DA9063_BCORE1: bcore1 {
|
||||
regulator-name = "BCORE1";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Maxim MAX77686 multi-function device
|
||||
|
||||
MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
|
||||
MAX77686 is a Multifunction device with PMIC, RTC and Charger on chip. It is
|
||||
interfaced to host controller using i2c interface. PMIC and Charger submodules
|
||||
are addressed using same i2c slave address whereas RTC submodule uses
|
||||
different i2c slave address,presently for which we are statically creating i2c
|
||||
|
|
|
@ -76,7 +76,60 @@ Optional properties:
|
|||
Valid values: 4300000, 4700000, 4800000, 4900000
|
||||
Default: 4300000
|
||||
|
||||
- led : the LED submodule device node
|
||||
|
||||
There are two LED outputs available - FLED1 and FLED2. Each of them can
|
||||
control a separate LED or they can be connected together to double
|
||||
the maximum current for a single connected LED. One LED is represented
|
||||
by one child node.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "maxim,max77693-led".
|
||||
|
||||
Optional properties:
|
||||
- maxim,boost-mode :
|
||||
In boost mode the device can produce up to 1.2A of total current
|
||||
on both outputs. The maximum current on each output is reduced
|
||||
to 625mA then. If not enabled explicitly, boost setting defaults to
|
||||
LEDS_BOOST_FIXED in case both current sources are used.
|
||||
Possible values:
|
||||
LEDS_BOOST_OFF (0) - no boost,
|
||||
LEDS_BOOST_ADAPTIVE (1) - adaptive mode,
|
||||
LEDS_BOOST_FIXED (2) - fixed mode.
|
||||
- maxim,boost-mvout : Output voltage of the boost module in millivolts.
|
||||
Valid values: 3300 - 5500, step by 25 (rounded down)
|
||||
Default: 3300
|
||||
- maxim,mvsys-min : Low input voltage level in millivolts. Flash is not fired
|
||||
if chip estimates that system voltage could drop below this level due
|
||||
to flash power consumption.
|
||||
Valid values: 2400 - 3400, step by 33 (rounded down)
|
||||
Default: 2400
|
||||
|
||||
Required properties for the LED child node:
|
||||
- led-sources : see Documentation/devicetree/bindings/leds/common.txt;
|
||||
device current output identifiers: 0 - FLED1, 1 - FLED2
|
||||
- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
|
||||
Valid values for a LED connected to one FLED output:
|
||||
15625 - 250000, step by 15625 (rounded down)
|
||||
Valid values for a LED connected to both FLED outputs:
|
||||
15625 - 500000, step by 15625 (rounded down)
|
||||
- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
|
||||
Valid values for a single LED connected to one FLED output
|
||||
(boost mode must be turned off):
|
||||
15625 - 1000000, step by 15625 (rounded down)
|
||||
Valid values for a single LED connected to both FLED outputs:
|
||||
15625 - 1250000, step by 15625 (rounded down)
|
||||
Valid values for two LEDs case:
|
||||
15625 - 625000, step by 15625 (rounded down)
|
||||
- flash-max-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
|
||||
Valid values: 62500 - 1000000, step by 62500 (rounded down)
|
||||
|
||||
Optional properties for the LED child node:
|
||||
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Example:
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
max77693@66 {
|
||||
compatible = "maxim,max77693";
|
||||
reg = <0x66>;
|
||||
|
@ -117,5 +170,19 @@ Example:
|
|||
maxim,thermal-regulation-celsius = <75>;
|
||||
maxim,battery-overcurrent-microamp = <3000000>;
|
||||
maxim,charge-input-threshold-microvolt = <4300000>;
|
||||
|
||||
led {
|
||||
compatible = "maxim,max77693-led";
|
||||
maxim,boost-mode = <LEDS_BOOST_FIXED>;
|
||||
maxim,boost-mvout = <5000>;
|
||||
maxim,mvsys-min = <2400>;
|
||||
|
||||
camera_flash: flash-led {
|
||||
label = "max77693-flash";
|
||||
led-sources = <0>, <1>;
|
||||
led-max-microamp = <500000>;
|
||||
flash-max-microamp = <1250000>;
|
||||
flash-max-timeout-us = <1000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
STMicroelectronics Low Power Controller (LPC) - RTC
|
||||
===================================================
|
||||
|
||||
LPC currently supports Watchdog OR Real Time Clock functionality.
|
||||
|
||||
[See: ../watchdog/st_lpc_wdt.txt for Watchdog options]
|
||||
|
||||
Required properties
|
||||
|
||||
- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
|
||||
"st,stih415-lpc" "st,stid127-lpc"
|
||||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
|
||||
ST_LPC_MODE_WDT [1]. One (and only one) mode must be
|
||||
selected.
|
||||
|
||||
Example:
|
||||
lpc@fde05000 {
|
||||
compatible = "st,stih407-lpc";
|
||||
reg = <0xfde05000 0x1000>;
|
||||
clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
|
||||
st,lpc-mode = <ST_LPC_MODE_RTC>;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
STMicroelectronics Low Power Controller (LPC) - Watchdog
|
||||
========================================================
|
||||
|
||||
LPC currently supports Watchdog OR Real Time Clock functionality.
|
||||
|
||||
[See: ../rtc/rtc-st-lpc.txt for RTC options]
|
||||
|
||||
Required properties
|
||||
|
||||
- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
|
||||
"st,stih415-lpc" "st,stid127-lpc"
|
||||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
|
||||
ST_LPC_MODE_WDT [1]. One (and only one) mode must be
|
||||
selected.
|
||||
|
||||
Required properties [watchdog mode]
|
||||
|
||||
- st,syscfg : Phandle to syscfg node used to enable watchdog and configure
|
||||
CPU reset type.
|
||||
- timeout-sec : Watchdog timeout in seconds
|
||||
|
||||
Optional properties [watchdog mode]
|
||||
|
||||
- st,warm-reset : If present reset type will be 'warm' - if not it will be cold
|
||||
|
||||
Example:
|
||||
lpc@fde05000 {
|
||||
compatible = "st,stih407-lpc";
|
||||
reg = <0xfde05000 0x1000>;
|
||||
clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
timeout-sec = <120>;
|
||||
st,lpc-mode = <ST_LPC_MODE_WDT>;
|
||||
st,warm-reset;
|
||||
};
|
17
MAINTAINERS
17
MAINTAINERS
|
@ -1489,10 +1489,12 @@ F: drivers/phy/phy-stih407-usb.c
|
|||
F: drivers/phy/phy-stih41x-usb.c
|
||||
F: drivers/pinctrl/pinctrl-st.c
|
||||
F: drivers/reset/sti/
|
||||
F: drivers/rtc/rtc-st-lpc.c
|
||||
F: drivers/tty/serial/st-asc.c
|
||||
F: drivers/usb/dwc3/dwc3-st.c
|
||||
F: drivers/usb/host/ehci-st.c
|
||||
F: drivers/usb/host/ohci-st.c
|
||||
F: drivers/watchdog/st_lpc_wdt.c
|
||||
F: drivers/ata/ahci_st.c
|
||||
|
||||
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
|
||||
|
@ -8581,14 +8583,20 @@ L: linux-fbdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/video/fbdev/s3c-fb.c
|
||||
|
||||
SAMSUNG MULTIFUNCTION DEVICE DRIVERS
|
||||
SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
|
||||
M: Sangbeom Kim <sbkim73@samsung.com>
|
||||
M: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
L: linux-samsung-soc@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/mfd/sec*.c
|
||||
F: drivers/regulator/s2m*.c
|
||||
F: drivers/regulator/s5m*.c
|
||||
F: drivers/clk/clk-s2mps11.c
|
||||
F: drivers/rtc/rtc-s5m.c
|
||||
F: include/linux/mfd/samsung/
|
||||
F: Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt
|
||||
F: Documentation/devicetree/bindings/mfd/s2mp*.txt
|
||||
|
||||
SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
|
||||
M: Kyungmin Park <kyungmin.park@samsung.com>
|
||||
|
@ -9627,6 +9635,13 @@ F: arch/arc/
|
|||
F: Documentation/devicetree/bindings/arc/
|
||||
F: drivers/tty/serial/arc_uart.c
|
||||
|
||||
SYSTEM CONFIGURATION (SYSCON)
|
||||
M: Lee Jones <lee.jones@linaro.org>
|
||||
M: Arnd Bergmann <arnd@arndb.de>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
|
||||
S: Supported
|
||||
F: drivers/mfd/syscon.c
|
||||
|
||||
SYSV FILESYSTEM
|
||||
M: Christoph Hellwig <hch@infradead.org>
|
||||
S: Maintained
|
||||
|
|
|
@ -1103,7 +1103,7 @@ config I2C_SIBYTE
|
|||
|
||||
config I2C_CROS_EC_TUNNEL
|
||||
tristate "ChromeOS EC tunnel I2C bus"
|
||||
depends on MFD_CROS_EC
|
||||
depends on CROS_EC_PROTO
|
||||
help
|
||||
If you say yes here you get an I2C bus that will tunnel i2c commands
|
||||
through to the other side of the ChromeOS EC to the i2c bus
|
||||
|
|
|
@ -182,8 +182,9 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
|
|||
const u16 bus_num = bus->remote_bus;
|
||||
int request_len;
|
||||
int response_len;
|
||||
int alloc_size;
|
||||
int result;
|
||||
struct cros_ec_command msg = { };
|
||||
struct cros_ec_command *msg;
|
||||
|
||||
request_len = ec_i2c_count_message(i2c_msgs, num);
|
||||
if (request_len < 0) {
|
||||
|
@ -198,25 +199,39 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
|
|||
return response_len;
|
||||
}
|
||||
|
||||
result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num);
|
||||
if (result)
|
||||
return result;
|
||||
alloc_size = max(request_len, response_len);
|
||||
msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg.version = 0;
|
||||
msg.command = EC_CMD_I2C_PASSTHRU;
|
||||
msg.outsize = request_len;
|
||||
msg.insize = response_len;
|
||||
result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num);
|
||||
if (result) {
|
||||
dev_err(dev, "Error constructing EC i2c message %d\n", result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
result = cros_ec_cmd_xfer(bus->ec, &msg);
|
||||
if (result < 0)
|
||||
return result;
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_I2C_PASSTHRU;
|
||||
msg->outsize = request_len;
|
||||
msg->insize = response_len;
|
||||
|
||||
result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num);
|
||||
if (result < 0)
|
||||
return result;
|
||||
result = cros_ec_cmd_xfer(bus->ec, msg);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Error transferring EC i2c message %d\n", result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Error parsing EC i2c message %d\n", result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Indicate success by saying how many messages were sent */
|
||||
return num;
|
||||
result = num;
|
||||
exit:
|
||||
kfree(msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
static u32 ec_i2c_functionality(struct i2c_adapter *adap)
|
||||
|
|
|
@ -677,7 +677,7 @@ config KEYBOARD_W90P910
|
|||
config KEYBOARD_CROS_EC
|
||||
tristate "ChromeOS EC keyboard"
|
||||
select INPUT_MATRIXKMAP
|
||||
depends on MFD_CROS_EC
|
||||
depends on CROS_EC_PROTO
|
||||
help
|
||||
Say Y here to enable the matrix keyboard used by ChromeOS devices
|
||||
and implemented on the ChromeOS EC. You must enable one bus option
|
||||
|
|
|
@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
|
|||
|
||||
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
|
||||
{
|
||||
int ret;
|
||||
struct cros_ec_command msg = {
|
||||
.command = EC_CMD_MKBP_STATE,
|
||||
.insize = ckdev->cols,
|
||||
};
|
||||
int ret = 0;
|
||||
struct cros_ec_command *msg;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(kb_state, msg.indata, ckdev->cols);
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_MKBP_STATE;
|
||||
msg->insize = ckdev->cols;
|
||||
msg->outsize = 0;
|
||||
|
||||
return 0;
|
||||
ret = cros_ec_cmd_xfer(ckdev->ec, msg);
|
||||
if (ret < 0) {
|
||||
dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(kb_state, msg->data, ckdev->cols);
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
|
||||
|
@ -266,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
|
|||
ckdev->dev = dev;
|
||||
dev_set_drvdata(&pdev->dev, ckdev);
|
||||
|
||||
idev->name = ec->ec_name;
|
||||
idev->name = CROS_EC_DEV_NAME;
|
||||
idev->phys = ec->phys_name;
|
||||
__set_bit(EV_REP, idev->evbit);
|
||||
|
||||
|
|
|
@ -566,7 +566,7 @@ static int pm860x_irq_domain_map(struct irq_domain *d, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops pm860x_irq_domain_ops = {
|
||||
static const struct irq_domain_ops pm860x_irq_domain_ops = {
|
||||
.map = pm860x_irq_domain_map,
|
||||
.xlate = irq_domain_xlate_onetwocell,
|
||||
};
|
||||
|
|
|
@ -52,7 +52,8 @@ config PMIC_ADP5520
|
|||
config MFD_AAT2870_CORE
|
||||
bool "AnalogicTech AAT2870"
|
||||
select MFD_CORE
|
||||
depends on I2C=y && GPIOLIB
|
||||
depends on I2C=y
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the AAT2870.
|
||||
This driver provides common support for accessing the device,
|
||||
|
@ -94,6 +95,8 @@ config MFD_AXP20X
|
|||
config MFD_CROS_EC
|
||||
tristate "ChromeOS Embedded Controller"
|
||||
select MFD_CORE
|
||||
select CHROME_PLATFORMS
|
||||
select CROS_EC_PROTO
|
||||
help
|
||||
If you say Y here you get support for the ChromeOS Embedded
|
||||
Controller (EC) providing keyboard, battery and power services.
|
||||
|
@ -102,7 +105,7 @@ config MFD_CROS_EC
|
|||
|
||||
config MFD_CROS_EC_I2C
|
||||
tristate "ChromeOS Embedded Controller (I2C)"
|
||||
depends on MFD_CROS_EC && I2C
|
||||
depends on MFD_CROS_EC && CROS_EC_PROTO && I2C
|
||||
|
||||
help
|
||||
If you say Y here, you get support for talking to the ChromeOS
|
||||
|
@ -112,7 +115,7 @@ config MFD_CROS_EC_I2C
|
|||
|
||||
config MFD_CROS_EC_SPI
|
||||
tristate "ChromeOS Embedded Controller (SPI)"
|
||||
depends on MFD_CROS_EC && SPI && OF
|
||||
depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF
|
||||
|
||||
---help---
|
||||
If you say Y here, you get support for talking to the ChromeOS EC
|
||||
|
@ -1115,7 +1118,8 @@ config MFD_TPS6586X
|
|||
|
||||
config MFD_TPS65910
|
||||
bool "TI TPS65910 Power Management chip"
|
||||
depends on I2C=y && GPIOLIB
|
||||
depends on I2C=y
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
|
|
|
@ -39,13 +39,13 @@ obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
|
|||
obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
|
||||
obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o
|
||||
obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o
|
||||
ifneq ($(CONFIG_MFD_WM5102),n)
|
||||
ifeq ($(CONFIG_MFD_WM5102),y)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o
|
||||
endif
|
||||
ifneq ($(CONFIG_MFD_WM5110),n)
|
||||
ifeq ($(CONFIG_MFD_WM5110),y)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o
|
||||
endif
|
||||
ifneq ($(CONFIG_MFD_WM8997),n)
|
||||
ifeq ($(CONFIG_MFD_WM8997),y)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += wm8997-tables.o
|
||||
endif
|
||||
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
|
||||
|
|
|
@ -574,7 +574,7 @@ static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops ab8500_irq_ops = {
|
||||
static const struct irq_domain_ops ab8500_irq_ops = {
|
||||
.map = ab8500_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -2885,7 +2885,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
|
|||
}
|
||||
|
||||
err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"ab8500-debug", &dev->kobj);
|
||||
if (err < 0) {
|
||||
pr_info("request_threaded_irq failed %d, %lu\n",
|
||||
|
|
|
@ -948,7 +948,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
if (gpadc->irq_sw >= 0) {
|
||||
ret = request_threaded_irq(gpadc->irq_sw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-sw",
|
||||
gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
|
@ -961,7 +962,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
if (gpadc->irq_hw >= 0) {
|
||||
ret = request_threaded_irq(gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-hw",
|
||||
gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
|
|
|
@ -250,20 +250,50 @@ static int arizona_wait_for_boot(struct arizona *arizona)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int arizona_apply_hardware_patch(struct arizona* arizona)
|
||||
static inline void arizona_enable_reset(struct arizona *arizona)
|
||||
{
|
||||
if (arizona->pdata.reset)
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 0);
|
||||
}
|
||||
|
||||
static void arizona_disable_reset(struct arizona *arizona)
|
||||
{
|
||||
if (arizona->pdata.reset) {
|
||||
switch (arizona->type) {
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
/* Meet requirements for minimum reset duration */
|
||||
msleep(5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 1);
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
struct arizona_sysclk_state {
|
||||
unsigned int fll;
|
||||
unsigned int sysclk;
|
||||
};
|
||||
|
||||
static int arizona_enable_freerun_sysclk(struct arizona *arizona,
|
||||
struct arizona_sysclk_state *state)
|
||||
{
|
||||
unsigned int fll, sysclk;
|
||||
int ret, err;
|
||||
|
||||
/* Cache existing FLL and SYSCLK settings */
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
|
||||
if (ret != 0) {
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &state->fll);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
|
||||
if (ret != 0) {
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
|
||||
&state->sysclk);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
|
@ -272,7 +302,7 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
|
|||
/* Start up SYSCLK using the FLL in free running mode */
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
|
||||
ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
|
||||
if (ret != 0) {
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to start FLL in freerunning mode: %d\n",
|
||||
ret);
|
||||
|
@ -281,53 +311,137 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
|
|||
ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
|
||||
ARIZONA_FLL1_CLOCK_OK_STS,
|
||||
ARIZONA_FLL1_CLOCK_OK_STS);
|
||||
if (ret != 0) {
|
||||
if (ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_fll;
|
||||
}
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
|
||||
if (ret != 0) {
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
|
||||
goto err_fll;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_fll:
|
||||
err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
|
||||
if (err)
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply old FLL settings: %d\n", err);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arizona_disable_freerun_sysclk(struct arizona *arizona,
|
||||
struct arizona_sysclk_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
|
||||
state->sysclk);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply old SYSCLK settings: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply old FLL settings: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm5102_apply_hardware_patch(struct arizona *arizona)
|
||||
{
|
||||
struct arizona_sysclk_state state;
|
||||
int err, ret;
|
||||
|
||||
ret = arizona_enable_freerun_sysclk(arizona, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start the write sequencer and wait for it to finish */
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
|
||||
ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
|
||||
if (ret != 0) {
|
||||
ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
|
||||
ret);
|
||||
goto err_sysclk;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
|
||||
ARIZONA_WSEQ_BUSY, 0);
|
||||
if (ret != 0) {
|
||||
if (ret) {
|
||||
regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
|
||||
ARIZONA_WSEQ_ABORT);
|
||||
ARIZONA_WSEQ_ABORT);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err_sysclk:
|
||||
err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
|
||||
if (err != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply old SYSCLK settings: %d\n",
|
||||
err);
|
||||
}
|
||||
err:
|
||||
err = arizona_disable_freerun_sysclk(arizona, &state);
|
||||
|
||||
err_fll:
|
||||
err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
|
||||
if (err != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply old FLL settings: %d\n",
|
||||
err);
|
||||
}
|
||||
return ret ?: err;
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
/*
|
||||
* Register patch to some of the CODECs internal write sequences
|
||||
* to ensure a clean exit from the low power sleep state.
|
||||
*/
|
||||
static const struct reg_default wm5110_sleep_patch[] = {
|
||||
{ 0x337A, 0xC100 },
|
||||
{ 0x337B, 0x0041 },
|
||||
{ 0x3300, 0xA210 },
|
||||
{ 0x3301, 0x050C },
|
||||
};
|
||||
|
||||
static int wm5110_apply_sleep_patch(struct arizona *arizona)
|
||||
{
|
||||
struct arizona_sysclk_state state;
|
||||
int err, ret;
|
||||
|
||||
ret = arizona_enable_freerun_sysclk(arizona, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return err;
|
||||
|
||||
ret = regmap_multi_reg_write_bypassed(arizona->regmap,
|
||||
wm5110_sleep_patch,
|
||||
ARRAY_SIZE(wm5110_sleep_patch));
|
||||
|
||||
err = arizona_disable_freerun_sysclk(arizona, &state);
|
||||
|
||||
return ret ?: err;
|
||||
}
|
||||
|
||||
static int wm5102_clear_write_sequencer(struct arizona *arizona)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3,
|
||||
0x0);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to clear write sequencer state: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
arizona_enable_reset(arizona);
|
||||
regulator_disable(arizona->dcvdd);
|
||||
|
||||
msleep(20);
|
||||
|
||||
ret = regulator_enable(arizona->dcvdd);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Failed to re-enable DCVDD: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
arizona_disable_reset(arizona);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -338,12 +452,33 @@ static int arizona_runtime_resume(struct device *dev)
|
|||
|
||||
dev_dbg(arizona->dev, "Leaving AoD mode\n");
|
||||
|
||||
if (arizona->has_fully_powered_off) {
|
||||
dev_dbg(arizona->dev, "Re-enabling core supplies\n");
|
||||
|
||||
ret = regulator_bulk_enable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable core supplies: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regulator_enable(arizona->dcvdd);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
|
||||
if (arizona->has_fully_powered_off)
|
||||
regulator_bulk_disable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (arizona->has_fully_powered_off) {
|
||||
arizona_disable_reset(arizona);
|
||||
enable_irq(arizona->irq);
|
||||
arizona->has_fully_powered_off = false;
|
||||
}
|
||||
|
||||
regcache_cache_only(arizona->regmap, false);
|
||||
|
||||
switch (arizona->type) {
|
||||
|
@ -366,14 +501,53 @@ static int arizona_runtime_resume(struct device *dev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = arizona_apply_hardware_patch(arizona);
|
||||
if (ret != 0) {
|
||||
ret = wm5102_apply_hardware_patch(arizona);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to apply hardware patch: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (arizona->external_dcvdd) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ISOLATION_CONTROL,
|
||||
ARIZONA_ISOLATE_DCVDD1, 0);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to connect DCVDD: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* As this is only called for the internal regulator
|
||||
* (where we know voltage ranges available) it is ok
|
||||
* to request an exact range.
|
||||
*/
|
||||
ret = regulator_set_voltage(arizona->dcvdd,
|
||||
1200000, 1200000);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to set resume voltage: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = wm5110_apply_sleep_patch(arizona);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to re-apply sleep patch: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret != 0) {
|
||||
|
@ -410,10 +584,17 @@ err:
|
|||
static int arizona_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
dev_dbg(arizona->dev, "Entering AoD mode\n");
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to check jack det status: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (arizona->external_dcvdd) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ISOLATION_CONTROL,
|
||||
|
@ -426,10 +607,56 @@ static int arizona_runtime_suspend(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
switch (arizona->type) {
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
if (arizona->external_dcvdd)
|
||||
break;
|
||||
|
||||
/*
|
||||
* As this is only called for the internal regulator
|
||||
* (where we know voltage ranges available) it is ok
|
||||
* to request an exact range.
|
||||
*/
|
||||
ret = regulator_set_voltage(arizona->dcvdd, 1175000, 1175000);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to set suspend voltage: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case WM5102:
|
||||
if (!(val & ARIZONA_JD1_ENA)) {
|
||||
ret = regmap_write(arizona->regmap,
|
||||
ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to clear write sequencer: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
regcache_cache_only(arizona->regmap, true);
|
||||
regcache_mark_dirty(arizona->regmap);
|
||||
regulator_disable(arizona->dcvdd);
|
||||
|
||||
/* Allow us to completely power down if no jack detection */
|
||||
if (!(val & ARIZONA_JD1_ENA)) {
|
||||
dev_dbg(arizona->dev, "Fully powering off\n");
|
||||
|
||||
arizona->has_fully_powered_off = true;
|
||||
|
||||
disable_irq(arizona->irq);
|
||||
arizona_enable_reset(arizona);
|
||||
regulator_bulk_disable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -728,9 +955,9 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
|
||||
if (arizona->pdata.reset) {
|
||||
/* Start out with /RESET low to put the chip into reset */
|
||||
ret = gpio_request_one(arizona->pdata.reset,
|
||||
GPIOF_DIR_OUT | GPIOF_INIT_LOW,
|
||||
"arizona /RESET");
|
||||
ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset,
|
||||
GPIOF_DIR_OUT | GPIOF_INIT_LOW,
|
||||
"arizona /RESET");
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to request /RESET: %d\n", ret);
|
||||
goto err_dcvdd;
|
||||
|
@ -751,10 +978,7 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
goto err_enable;
|
||||
}
|
||||
|
||||
if (arizona->pdata.reset) {
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 1);
|
||||
msleep(1);
|
||||
}
|
||||
arizona_disable_reset(arizona);
|
||||
|
||||
regcache_cache_only(arizona->regmap, false);
|
||||
|
||||
|
@ -777,8 +1001,6 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
|
||||
/* If we have a /RESET GPIO we'll already be reset */
|
||||
if (!arizona->pdata.reset) {
|
||||
regcache_mark_dirty(arizona->regmap);
|
||||
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
|
@ -786,12 +1008,6 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
}
|
||||
|
||||
msleep(1);
|
||||
|
||||
ret = regcache_sync(arizona->regmap);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to sync device: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure device startup is complete */
|
||||
|
@ -799,21 +1015,24 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
case WM5102:
|
||||
ret = regmap_read(arizona->regmap,
|
||||
ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
|
||||
if (ret != 0)
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to check write sequencer state: %d\n",
|
||||
ret);
|
||||
else if (val & 0x01)
|
||||
break;
|
||||
/* Fall through */
|
||||
default:
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Device failed initial boot: %d\n", ret);
|
||||
goto err_reset;
|
||||
} else if (val & 0x01) {
|
||||
ret = wm5102_clear_write_sequencer(arizona);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
/* Read the device ID information & do device specific stuff */
|
||||
|
@ -891,14 +1110,24 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
|
||||
switch (arizona->type) {
|
||||
case WM5102:
|
||||
ret = arizona_apply_hardware_patch(arizona);
|
||||
if (ret != 0) {
|
||||
ret = wm5102_apply_hardware_patch(arizona);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to apply hardware patch: %d\n",
|
||||
ret);
|
||||
goto err_reset;
|
||||
}
|
||||
break;
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
ret = wm5110_apply_sleep_patch(arizona);
|
||||
if (ret) {
|
||||
dev_err(arizona->dev,
|
||||
"Failed to apply sleep patch: %d\n",
|
||||
ret);
|
||||
goto err_reset;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -977,12 +1206,16 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
/* Default for both is 0 so noop with defaults */
|
||||
val = arizona->pdata.dmic_ref[i]
|
||||
<< ARIZONA_IN1_DMIC_SUP_SHIFT;
|
||||
val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
|
||||
if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC)
|
||||
val |= 1 << ARIZONA_IN1_MODE_SHIFT;
|
||||
if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
|
||||
val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_IN1L_CONTROL + (i * 8),
|
||||
ARIZONA_IN1_DMIC_SUP_MASK |
|
||||
ARIZONA_IN1_MODE_MASK, val);
|
||||
ARIZONA_IN1_MODE_MASK |
|
||||
ARIZONA_IN1_SINGLE_ENDED_MASK, val);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
|
||||
|
@ -1054,10 +1287,7 @@ int arizona_dev_init(struct arizona *arizona)
|
|||
err_irq:
|
||||
arizona_irq_exit(arizona);
|
||||
err_reset:
|
||||
if (arizona->pdata.reset) {
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 0);
|
||||
gpio_free(arizona->pdata.reset);
|
||||
}
|
||||
arizona_enable_reset(arizona);
|
||||
regulator_disable(arizona->dcvdd);
|
||||
err_enable:
|
||||
regulator_bulk_disable(arizona->num_core_supplies,
|
||||
|
@ -1082,8 +1312,7 @@ int arizona_dev_exit(struct arizona *arizona)
|
|||
arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
|
||||
arizona_irq_exit(arizona);
|
||||
if (arizona->pdata.reset)
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 0);
|
||||
arizona_enable_reset(arizona);
|
||||
|
||||
regulator_bulk_disable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
|
|
|
@ -186,7 +186,7 @@ static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops arizona_domain_ops = {
|
||||
static const struct irq_domain_ops arizona_domain_ops = {
|
||||
.map = arizona_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
static const char * const axp20x_model_names[] = {
|
||||
"AXP202",
|
||||
"AXP209",
|
||||
"AXP221",
|
||||
"AXP288",
|
||||
};
|
||||
|
||||
|
@ -54,6 +55,25 @@ static const struct regmap_access_table axp20x_volatile_table = {
|
|||
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range axp22x_writeable_ranges[] = {
|
||||
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
|
||||
regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
|
||||
};
|
||||
|
||||
static const struct regmap_range axp22x_volatile_ranges[] = {
|
||||
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table axp22x_writeable_table = {
|
||||
.yes_ranges = axp22x_writeable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(axp22x_writeable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table axp22x_volatile_table = {
|
||||
.yes_ranges = axp22x_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges),
|
||||
};
|
||||
|
||||
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),
|
||||
|
@ -87,6 +107,20 @@ static struct resource axp20x_pek_resources[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct resource axp22x_pek_resources[] = {
|
||||
{
|
||||
.name = "PEK_DBR",
|
||||
.start = AXP22X_IRQ_PEK_RIS_EDGE,
|
||||
.end = AXP22X_IRQ_PEK_RIS_EDGE,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}, {
|
||||
.name = "PEK_DBF",
|
||||
.start = AXP22X_IRQ_PEK_FAL_EDGE,
|
||||
.end = AXP22X_IRQ_PEK_FAL_EDGE,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource axp288_fuel_gauge_resources[] = {
|
||||
{
|
||||
.start = AXP288_IRQ_QWBTU,
|
||||
|
@ -129,6 +163,15 @@ static const struct regmap_config axp20x_regmap_config = {
|
|||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_config axp22x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.wr_table = &axp22x_writeable_table,
|
||||
.volatile_table = &axp22x_volatile_table,
|
||||
.max_register = AXP22X_BATLOW_THRES1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_config axp288_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
@ -181,6 +224,34 @@ static const struct regmap_irq axp20x_regmap_irqs[] = {
|
|||
INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0),
|
||||
};
|
||||
|
||||
static const struct regmap_irq axp22x_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V, 0, 7),
|
||||
INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN, 0, 6),
|
||||
INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL, 0, 5),
|
||||
INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V, 0, 4),
|
||||
INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN, 0, 3),
|
||||
INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL, 0, 2),
|
||||
INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW, 0, 1),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN, 1, 7),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL, 1, 6),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE, 1, 5),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE, 1, 4),
|
||||
INIT_REGMAP_IRQ(AXP22X, CHARG, 1, 3),
|
||||
INIT_REGMAP_IRQ(AXP22X, CHARG_DONE, 1, 2),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH, 1, 1),
|
||||
INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW, 1, 0),
|
||||
INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH, 2, 7),
|
||||
INIT_REGMAP_IRQ(AXP22X, PEK_SHORT, 2, 1),
|
||||
INIT_REGMAP_IRQ(AXP22X, PEK_LONG, 2, 0),
|
||||
INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1, 3, 1),
|
||||
INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2, 3, 0),
|
||||
INIT_REGMAP_IRQ(AXP22X, TIMER, 4, 7),
|
||||
INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE, 4, 6),
|
||||
INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE, 4, 5),
|
||||
INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT, 4, 1),
|
||||
INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT, 4, 0),
|
||||
};
|
||||
|
||||
/* some IRQs are compatible with axp20x models */
|
||||
static const struct regmap_irq axp288_regmap_irqs[] = {
|
||||
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
|
||||
|
@ -224,6 +295,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
|
|||
static const struct of_device_id axp20x_of_match[] = {
|
||||
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
|
||||
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
|
||||
{ .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_of_match);
|
||||
|
@ -258,6 +330,18 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
|
|||
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
|
||||
.name = "axp22x_irq_chip",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
.ack_base = AXP20X_IRQ1_STATE,
|
||||
.mask_base = AXP20X_IRQ1_EN,
|
||||
.mask_invert = true,
|
||||
.init_ack_masked = true,
|
||||
.irqs = axp22x_regmap_irqs,
|
||||
.num_irqs = ARRAY_SIZE(axp22x_regmap_irqs),
|
||||
.num_regs = 5,
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip axp288_regmap_irq_chip = {
|
||||
.name = "axp288_irq_chip",
|
||||
.status_base = AXP20X_IRQ1_STATE,
|
||||
|
@ -281,6 +365,16 @@ static struct mfd_cell axp20x_cells[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell axp22x_cells[] = {
|
||||
{
|
||||
.name = "axp20x-pek",
|
||||
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
|
||||
.resources = axp22x_pek_resources,
|
||||
}, {
|
||||
.name = "axp20x-regulator",
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource axp288_adc_resources[] = {
|
||||
{
|
||||
.name = "GPADC",
|
||||
|
@ -426,6 +520,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
|
|||
axp20x->regmap_cfg = &axp20x_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
|
||||
break;
|
||||
case AXP221_ID:
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
|
||||
axp20x->cells = axp22x_cells;
|
||||
axp20x->regmap_cfg = &axp22x_regmap_config;
|
||||
axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
|
||||
break;
|
||||
case AXP288_ID:
|
||||
axp20x->cells = axp288_cells;
|
||||
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
|
||||
|
|
|
@ -17,111 +17,36 @@
|
|||
* battery charging and regulator control, firmware update.
|
||||
*/
|
||||
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define EC_COMMAND_RETRIES 50
|
||||
#define CROS_EC_DEV_EC_INDEX 0
|
||||
#define CROS_EC_DEV_PD_INDEX 1
|
||||
|
||||
int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
uint8_t *out;
|
||||
int csum, i;
|
||||
static struct cros_ec_platform ec_p = {
|
||||
.ec_name = CROS_EC_DEV_NAME,
|
||||
.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
|
||||
};
|
||||
|
||||
BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
|
||||
out = ec_dev->dout;
|
||||
out[0] = EC_CMD_VERSION0 + msg->version;
|
||||
out[1] = msg->command;
|
||||
out[2] = msg->outsize;
|
||||
csum = out[0] + out[1] + out[2];
|
||||
for (i = 0; i < msg->outsize; i++)
|
||||
csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i];
|
||||
out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff);
|
||||
static struct cros_ec_platform pd_p = {
|
||||
.ec_name = CROS_EC_DEV_PD_NAME,
|
||||
.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
|
||||
};
|
||||
|
||||
return EC_MSG_TX_PROTO_BYTES + msg->outsize;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_prepare_tx);
|
||||
static const struct mfd_cell ec_cell = {
|
||||
.name = "cros-ec-ctl",
|
||||
.platform_data = &ec_p,
|
||||
.pdata_size = sizeof(ec_p),
|
||||
};
|
||||
|
||||
int cros_ec_check_result(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
switch (msg->result) {
|
||||
case EC_RES_SUCCESS:
|
||||
return 0;
|
||||
case EC_RES_IN_PROGRESS:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
|
||||
msg->command);
|
||||
return -EAGAIN;
|
||||
default:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
|
||||
msg->command, msg->result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_check_result);
|
||||
|
||||
int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ec_dev->lock);
|
||||
ret = ec_dev->cmd_xfer(ec_dev, msg);
|
||||
if (msg->result == EC_RES_IN_PROGRESS) {
|
||||
int i;
|
||||
struct cros_ec_command status_msg = { };
|
||||
struct ec_response_get_comms_status *status;
|
||||
|
||||
status_msg.command = EC_CMD_GET_COMMS_STATUS;
|
||||
status_msg.insize = sizeof(*status);
|
||||
|
||||
/*
|
||||
* Query the EC's status until it's no longer busy or
|
||||
* we encounter an error.
|
||||
*/
|
||||
for (i = 0; i < EC_COMMAND_RETRIES; i++) {
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
ret = ec_dev->cmd_xfer(ec_dev, &status_msg);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
msg->result = status_msg.result;
|
||||
if (status_msg.result != EC_RES_SUCCESS)
|
||||
break;
|
||||
|
||||
status = (struct ec_response_get_comms_status *)
|
||||
status_msg.indata;
|
||||
if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ec_dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_cmd_xfer);
|
||||
|
||||
static const struct mfd_cell cros_devs[] = {
|
||||
{
|
||||
.name = "cros-ec-keyb",
|
||||
.id = 1,
|
||||
.of_compatible = "google,cros-ec-keyb",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-i2c-tunnel",
|
||||
.id = 2,
|
||||
.of_compatible = "google,cros-ec-i2c-tunnel",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-ctl",
|
||||
.id = 3,
|
||||
},
|
||||
static const struct mfd_cell ec_pd_cell = {
|
||||
.name = "cros-ec-ctl",
|
||||
.platform_data = &pd_p,
|
||||
.pdata_size = sizeof(pd_p),
|
||||
};
|
||||
|
||||
int cros_ec_register(struct cros_ec_device *ec_dev)
|
||||
|
@ -129,27 +54,59 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
|
|||
struct device *dev = ec_dev->dev;
|
||||
int err = 0;
|
||||
|
||||
if (ec_dev->din_size) {
|
||||
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
|
||||
if (!ec_dev->din)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ec_dev->dout_size) {
|
||||
ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
|
||||
if (!ec_dev->dout)
|
||||
return -ENOMEM;
|
||||
}
|
||||
ec_dev->max_request = sizeof(struct ec_params_hello);
|
||||
ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->max_passthru = 0;
|
||||
|
||||
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
|
||||
if (!ec_dev->din)
|
||||
return -ENOMEM;
|
||||
|
||||
ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
|
||||
if (!ec_dev->dout)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ec_dev->lock);
|
||||
|
||||
err = mfd_add_devices(dev, 0, cros_devs,
|
||||
ARRAY_SIZE(cros_devs),
|
||||
cros_ec_query_all(ec_dev);
|
||||
|
||||
err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
|
||||
NULL, ec_dev->irq, NULL);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to add mfd devices\n");
|
||||
dev_err(dev,
|
||||
"Failed to register Embedded Controller subdevice %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ec_dev->max_passthru) {
|
||||
/*
|
||||
* Register a PD device as well on top of this device.
|
||||
* We make the following assumptions:
|
||||
* - behind an EC, we have a pd
|
||||
* - only one device added.
|
||||
* - the EC is responsive at init time (it is not true for a
|
||||
* sensor hub.
|
||||
*/
|
||||
err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
|
||||
&ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Failed to register Power Delivery subdevice %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
|
||||
err = of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
if (err) {
|
||||
mfd_remove_devices(dev);
|
||||
dev_err(dev, "Failed to register sub-devices\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "Chrome EC device registered\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
|
@ -22,6 +23,32 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* Request format for protocol v3
|
||||
* byte 0 0xda (EC_COMMAND_PROTOCOL_3)
|
||||
* byte 1-8 struct ec_host_request
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_request_i2c {
|
||||
/* Always 0xda to backward compatible with v2 struct */
|
||||
uint8_t command_protocol;
|
||||
struct ec_host_request ec_request;
|
||||
} __packed;
|
||||
|
||||
|
||||
/*
|
||||
* Response format for protocol v3
|
||||
* byte 0 result code
|
||||
* byte 1 packet_length
|
||||
* byte 2-9 struct ec_host_response
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_response_i2c {
|
||||
uint8_t result;
|
||||
uint8_t packet_length;
|
||||
struct ec_host_response ec_response;
|
||||
} __packed;
|
||||
|
||||
static inline struct cros_ec_device *to_ec_dev(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
@ -29,6 +56,134 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev)
|
|||
return i2c_get_clientdata(client);
|
||||
}
|
||||
|
||||
static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct i2c_client *client = ec_dev->priv;
|
||||
int ret = -ENOMEM;
|
||||
int i;
|
||||
int packet_len;
|
||||
u8 *out_buf = NULL;
|
||||
u8 *in_buf = NULL;
|
||||
u8 sum;
|
||||
struct i2c_msg i2c_msg[2];
|
||||
struct ec_host_response *ec_response;
|
||||
struct ec_host_request_i2c *ec_request_i2c;
|
||||
struct ec_host_response_i2c *ec_response_i2c;
|
||||
int request_header_size = sizeof(struct ec_host_request_i2c);
|
||||
int response_header_size = sizeof(struct ec_host_response_i2c);
|
||||
|
||||
i2c_msg[0].addr = client->addr;
|
||||
i2c_msg[0].flags = 0;
|
||||
i2c_msg[1].addr = client->addr;
|
||||
i2c_msg[1].flags = I2C_M_RD;
|
||||
|
||||
packet_len = msg->insize + response_header_size;
|
||||
BUG_ON(packet_len > ec_dev->din_size);
|
||||
in_buf = ec_dev->din;
|
||||
i2c_msg[1].len = packet_len;
|
||||
i2c_msg[1].buf = (char *) in_buf;
|
||||
|
||||
packet_len = msg->outsize + request_header_size;
|
||||
BUG_ON(packet_len > ec_dev->dout_size);
|
||||
out_buf = ec_dev->dout;
|
||||
i2c_msg[0].len = packet_len;
|
||||
i2c_msg[0].buf = (char *) out_buf;
|
||||
|
||||
/* create request data */
|
||||
ec_request_i2c = (struct ec_host_request_i2c *) out_buf;
|
||||
ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
|
||||
|
||||
ec_dev->dout++;
|
||||
ret = cros_ec_prepare_tx(ec_dev, msg);
|
||||
ec_dev->dout--;
|
||||
|
||||
/* send command to EC and read answer */
|
||||
ret = i2c_transfer(client->adapter, i2c_msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_dbg(ec_dev->dev, "i2c transfer failed: %d\n", ret);
|
||||
goto done;
|
||||
} else if (ret != 2) {
|
||||
dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ec_response_i2c = (struct ec_host_response_i2c *) in_buf;
|
||||
msg->result = ec_response_i2c->result;
|
||||
ec_response = &ec_response_i2c->ec_response;
|
||||
|
||||
switch (msg->result) {
|
||||
case EC_RES_SUCCESS:
|
||||
break;
|
||||
case EC_RES_IN_PROGRESS:
|
||||
ret = -EAGAIN;
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
|
||||
msg->command);
|
||||
goto done;
|
||||
|
||||
default:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
|
||||
msg->command, msg->result);
|
||||
/*
|
||||
* When we send v3 request to v2 ec, ec won't recognize the
|
||||
* 0xda (EC_COMMAND_PROTOCOL_3) and will return with status
|
||||
* EC_RES_INVALID_COMMAND with zero data length.
|
||||
*
|
||||
* In case of invalid command for v3 protocol the data length
|
||||
* will be at least sizeof(struct ec_host_response)
|
||||
*/
|
||||
if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
|
||||
ec_response_i2c->packet_length == 0) {
|
||||
ret = -EPROTONOSUPPORT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
|
||||
dev_err(ec_dev->dev,
|
||||
"response of %u bytes too short; not a full header\n",
|
||||
ec_response_i2c->packet_length);
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (msg->insize < ec_response->data_len) {
|
||||
dev_err(ec_dev->dev,
|
||||
"response data size is too large: expected %u, got %u\n",
|
||||
msg->insize,
|
||||
ec_response->data_len);
|
||||
ret = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
sum = 0;
|
||||
for (i = 0; i < sizeof(struct ec_host_response); i++)
|
||||
sum += ((u8 *)ec_response)[i];
|
||||
|
||||
memcpy(msg->data,
|
||||
in_buf + response_header_size,
|
||||
ec_response->data_len);
|
||||
for (i = 0; i < ec_response->data_len; i++)
|
||||
sum += msg->data[i];
|
||||
|
||||
/* All bytes should sum to zero */
|
||||
if (sum) {
|
||||
dev_err(ec_dev->dev, "bad packet checksum\n");
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = ec_response->data_len;
|
||||
|
||||
done:
|
||||
if (msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
|
@ -76,7 +231,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
|
|||
/* copy message payload and compute checksum */
|
||||
sum = out_buf[0] + out_buf[1] + out_buf[2];
|
||||
for (i = 0; i < msg->outsize; i++) {
|
||||
out_buf[3 + i] = msg->outdata[i];
|
||||
out_buf[3 + i] = msg->data[i];
|
||||
sum += out_buf[3 + i];
|
||||
}
|
||||
out_buf[3 + msg->outsize] = sum;
|
||||
|
@ -109,7 +264,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
|
|||
/* copy response packet payload and compute checksum */
|
||||
sum = in_buf[0] + in_buf[1];
|
||||
for (i = 0; i < len; i++) {
|
||||
msg->indata[i] = in_buf[2 + i];
|
||||
msg->data[i] = in_buf[2 + i];
|
||||
sum += in_buf[2 + i];
|
||||
}
|
||||
dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
|
||||
|
@ -121,9 +276,12 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
|
|||
}
|
||||
|
||||
ret = len;
|
||||
done:
|
||||
done:
|
||||
kfree(in_buf);
|
||||
kfree(out_buf);
|
||||
if (msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -143,9 +301,11 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
|
|||
ec_dev->priv = client;
|
||||
ec_dev->irq = client->irq;
|
||||
ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c;
|
||||
ec_dev->ec_name = client->name;
|
||||
ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c;
|
||||
ec_dev->phys_name = client->adapter->name;
|
||||
ec_dev->parent = &client->dev;
|
||||
ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
|
||||
sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
|
||||
|
||||
err = cros_ec_register(ec_dev);
|
||||
if (err) {
|
||||
|
|
|
@ -65,29 +65,26 @@
|
|||
*/
|
||||
#define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
|
||||
|
||||
/*
|
||||
* The EC is unresponsive for a time after a reboot command. Add a
|
||||
* simple delay to make sure that the bus stays locked.
|
||||
*/
|
||||
#define EC_REBOOT_DELAY_MS 50
|
||||
|
||||
/**
|
||||
* struct cros_ec_spi - information about a SPI-connected EC
|
||||
*
|
||||
* @spi: SPI device we are connected to
|
||||
* @last_transfer_ns: time that we last finished a transfer, or 0 if there
|
||||
* if no record
|
||||
* @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that
|
||||
* is sent when we want to turn on CS at the start of a transaction.
|
||||
* @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that
|
||||
* is sent when we want to turn off CS at the end of a transaction.
|
||||
*/
|
||||
struct cros_ec_spi {
|
||||
struct spi_device *spi;
|
||||
s64 last_transfer_ns;
|
||||
unsigned int start_of_msg_delay;
|
||||
unsigned int end_of_msg_delay;
|
||||
};
|
||||
|
||||
static void debug_packet(struct device *dev, const char *name, u8 *ptr,
|
||||
int len)
|
||||
int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
|
@ -100,6 +97,172 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
|
|||
#endif
|
||||
}
|
||||
|
||||
static int terminate_request(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer trans;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Turn off CS, possibly adding a delay to ensure the rising edge
|
||||
* doesn't come too soon after the end of the data.
|
||||
*/
|
||||
spi_message_init(&msg);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.delay_usecs = ec_spi->end_of_msg_delay;
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
|
||||
ret = spi_sync(ec_spi->spi, &msg);
|
||||
|
||||
/* Reset end-of-response timer */
|
||||
ec_spi->last_transfer_ns = ktime_get_ns();
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev,
|
||||
"cs-deassert spi transfer failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* receive_n_bytes - receive n bytes from the EC.
|
||||
*
|
||||
* Assumes buf is a pointer into the ec_dev->din buffer
|
||||
*/
|
||||
static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans;
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
|
||||
BUG_ON(buf - ec_dev->din + n > ec_dev->din_size);
|
||||
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.cs_change = 1;
|
||||
trans.rx_buf = buf;
|
||||
trans.len = n;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync(ec_spi->spi, &msg);
|
||||
if (ret < 0)
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_spi_receive_packet - Receive a packet from the EC.
|
||||
*
|
||||
* This function has two phases: reading the preamble bytes (since if we read
|
||||
* data from the EC before it is ready to send, we just get preamble) and
|
||||
* reading the actual message.
|
||||
*
|
||||
* The received data is placed into ec_dev->din.
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @need_len: Number of message bytes we need to read
|
||||
*/
|
||||
static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
|
||||
int need_len)
|
||||
{
|
||||
struct ec_host_response *response;
|
||||
u8 *ptr, *end;
|
||||
int ret;
|
||||
unsigned long deadline;
|
||||
int todo;
|
||||
|
||||
BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
|
||||
|
||||
/* Receive data until we see the header byte */
|
||||
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
|
||||
while (true) {
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
ret = receive_n_bytes(ec_dev,
|
||||
ec_dev->din,
|
||||
EC_MSG_PREAMBLE_COUNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
|
||||
if (*ptr == EC_SPI_FRAME_START) {
|
||||
dev_dbg(ec_dev->dev, "msg found at %zd\n",
|
||||
ptr - ec_dev->din);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptr != end)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Use the time at the start of the loop as a timeout. This
|
||||
* gives us one last shot at getting the transfer and is useful
|
||||
* in case we got context switched out for a while.
|
||||
*/
|
||||
if (time_after(start_jiffies, deadline)) {
|
||||
dev_warn(ec_dev->dev, "EC failed to respond in time\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ptr now points to the header byte. Copy any valid data to the
|
||||
* start of our buffer
|
||||
*/
|
||||
todo = end - ++ptr;
|
||||
BUG_ON(todo < 0 || todo > ec_dev->din_size);
|
||||
todo = min(todo, need_len);
|
||||
memmove(ec_dev->din, ptr, todo);
|
||||
ptr = ec_dev->din + todo;
|
||||
dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
|
||||
need_len, todo);
|
||||
need_len -= todo;
|
||||
|
||||
/* If the entire response struct wasn't read, get the rest of it. */
|
||||
if (todo < sizeof(*response)) {
|
||||
ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo);
|
||||
if (ret < 0)
|
||||
return -EBADMSG;
|
||||
ptr += (sizeof(*response) - todo);
|
||||
todo = sizeof(*response);
|
||||
}
|
||||
|
||||
response = (struct ec_host_response *)ec_dev->din;
|
||||
|
||||
/* Abort if data_len is too large. */
|
||||
if (response->data_len > ec_dev->din_size)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/* Receive data until we have it all */
|
||||
while (need_len > 0) {
|
||||
/*
|
||||
* We can't support transfers larger than the SPI FIFO size
|
||||
* unless we have DMA. We don't have DMA on the ISP SPI ports
|
||||
* for Exynos. We need a way of asking SPI driver for
|
||||
* maximum-supported transfer size.
|
||||
*/
|
||||
todo = min(need_len, 256);
|
||||
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
|
||||
todo, need_len, ptr - ec_dev->din);
|
||||
|
||||
ret = receive_n_bytes(ec_dev, ptr, todo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ptr += todo;
|
||||
need_len -= todo;
|
||||
}
|
||||
|
||||
dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_spi_receive_response - Receive a response from the EC.
|
||||
*
|
||||
|
@ -115,34 +278,27 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
|
|||
static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
|
||||
int need_len)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans;
|
||||
struct spi_message msg;
|
||||
u8 *ptr, *end;
|
||||
int ret;
|
||||
unsigned long deadline;
|
||||
int todo;
|
||||
|
||||
BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
|
||||
|
||||
/* Receive data until we see the header byte */
|
||||
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
|
||||
while (true) {
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.cs_change = 1;
|
||||
trans.rx_buf = ptr = ec_dev->din;
|
||||
trans.len = EC_MSG_PREAMBLE_COUNT;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync(ec_spi->spi, &msg);
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
ret = receive_n_bytes(ec_dev,
|
||||
ec_dev->din,
|
||||
EC_MSG_PREAMBLE_COUNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptr = ec_dev->din;
|
||||
for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
|
||||
if (*ptr == EC_MSG_HEADER) {
|
||||
if (*ptr == EC_SPI_FRAME_START) {
|
||||
dev_dbg(ec_dev->dev, "msg found at %zd\n",
|
||||
ptr - ec_dev->din);
|
||||
break;
|
||||
|
@ -187,21 +343,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
|
|||
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
|
||||
todo, need_len, ptr - ec_dev->din);
|
||||
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.cs_change = 1;
|
||||
trans.rx_buf = ptr;
|
||||
trans.len = todo;
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
|
||||
/* send command to EC and read answer */
|
||||
BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo >
|
||||
ec_dev->din_size);
|
||||
ret = spi_sync(ec_spi->spi, &msg);
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
ret = receive_n_bytes(ec_dev, ptr, todo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
debug_packet(ec_dev->dev, "interim", ptr, todo);
|
||||
ptr += todo;
|
||||
|
@ -213,6 +357,138 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @ec_msg: Message to transfer
|
||||
*/
|
||||
static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *ec_msg)
|
||||
{
|
||||
struct ec_host_request *request;
|
||||
struct ec_host_response *response;
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans, trans_delay;
|
||||
struct spi_message msg;
|
||||
int i, len;
|
||||
u8 *ptr;
|
||||
u8 *rx_buf;
|
||||
u8 sum;
|
||||
int ret = 0, final_ret;
|
||||
|
||||
len = cros_ec_prepare_tx(ec_dev, ec_msg);
|
||||
request = (struct ec_host_request *)ec_dev->dout;
|
||||
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
|
||||
|
||||
/* If it's too soon to do another transaction, wait */
|
||||
if (ec_spi->last_transfer_ns) {
|
||||
unsigned long delay; /* The delay completed so far */
|
||||
|
||||
delay = ktime_get_ns() - ec_spi->last_transfer_ns;
|
||||
if (delay < EC_SPI_RECOVERY_TIME_NS)
|
||||
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
|
||||
}
|
||||
|
||||
rx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!rx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Leave a gap between CS assertion and clocking of data to allow the
|
||||
* EC time to wakeup.
|
||||
*/
|
||||
spi_message_init(&msg);
|
||||
if (ec_spi->start_of_msg_delay) {
|
||||
memset(&trans_delay, 0, sizeof(trans_delay));
|
||||
trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
|
||||
spi_message_add_tail(&trans_delay, &msg);
|
||||
}
|
||||
|
||||
/* Transmit phase - send our message */
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.tx_buf = ec_dev->dout;
|
||||
trans.rx_buf = rx_buf;
|
||||
trans.len = len;
|
||||
trans.cs_change = 1;
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync(ec_spi->spi, &msg);
|
||||
|
||||
/* Get the response */
|
||||
if (!ret) {
|
||||
/* Verify that EC can process command */
|
||||
for (i = 0; i < len; i++) {
|
||||
switch (rx_buf[i]) {
|
||||
case EC_SPI_PAST_END:
|
||||
case EC_SPI_RX_BAD_DATA:
|
||||
case EC_SPI_NOT_READY:
|
||||
ret = -EAGAIN;
|
||||
ec_msg->result = EC_RES_IN_PROGRESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
if (!ret)
|
||||
ret = cros_ec_spi_receive_packet(ec_dev,
|
||||
ec_msg->insize + sizeof(*response));
|
||||
} else {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
}
|
||||
|
||||
final_ret = terminate_request(ec_dev);
|
||||
if (!ret)
|
||||
ret = final_ret;
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
|
||||
/* check response error code */
|
||||
response = (struct ec_host_response *)ptr;
|
||||
ec_msg->result = response->result;
|
||||
|
||||
ret = cros_ec_check_result(ec_dev, ec_msg);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
len = response->data_len;
|
||||
sum = 0;
|
||||
if (len > ec_msg->insize) {
|
||||
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
|
||||
len, ec_msg->insize);
|
||||
ret = -EMSGSIZE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(*response); i++)
|
||||
sum += ptr[i];
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
memcpy(ec_msg->data, ptr + sizeof(*response), len);
|
||||
for (i = 0; i < len; i++)
|
||||
sum += ec_msg->data[i];
|
||||
|
||||
if (sum) {
|
||||
dev_err(ec_dev->dev,
|
||||
"bad packet checksum, calculated %x\n",
|
||||
sum);
|
||||
ret = -EBADMSG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
exit:
|
||||
kfree(rx_buf);
|
||||
if (ec_msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
|
||||
*
|
||||
|
@ -227,6 +503,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
|||
struct spi_message msg;
|
||||
int i, len;
|
||||
u8 *ptr;
|
||||
u8 *rx_buf;
|
||||
int sum;
|
||||
int ret = 0, final_ret;
|
||||
|
||||
|
@ -242,10 +519,17 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
|||
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
|
||||
}
|
||||
|
||||
rx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!rx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Transmit phase - send our message */
|
||||
debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.tx_buf = ec_dev->dout;
|
||||
trans.rx_buf = rx_buf;
|
||||
trans.len = len;
|
||||
trans.cs_change = 1;
|
||||
spi_message_init(&msg);
|
||||
|
@ -254,29 +538,32 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
|||
|
||||
/* Get the response */
|
||||
if (!ret) {
|
||||
ret = cros_ec_spi_receive_response(ec_dev,
|
||||
ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
|
||||
/* Verify that EC can process command */
|
||||
for (i = 0; i < len; i++) {
|
||||
switch (rx_buf[i]) {
|
||||
case EC_SPI_PAST_END:
|
||||
case EC_SPI_RX_BAD_DATA:
|
||||
case EC_SPI_NOT_READY:
|
||||
ret = -EAGAIN;
|
||||
ec_msg->result = EC_RES_IN_PROGRESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
if (!ret)
|
||||
ret = cros_ec_spi_receive_response(ec_dev,
|
||||
ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
|
||||
} else {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off CS, possibly adding a delay to ensure the rising edge
|
||||
* doesn't come too soon after the end of the data.
|
||||
*/
|
||||
spi_message_init(&msg);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.delay_usecs = ec_spi->end_of_msg_delay;
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
|
||||
final_ret = spi_sync(ec_spi->spi, &msg);
|
||||
ec_spi->last_transfer_ns = ktime_get_ns();
|
||||
final_ret = terminate_request(ec_dev);
|
||||
if (!ret)
|
||||
ret = final_ret;
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ptr = ec_dev->din;
|
||||
|
||||
|
@ -299,7 +586,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
|||
for (i = 0; i < len; i++) {
|
||||
sum += ptr[i + 2];
|
||||
if (ec_msg->insize)
|
||||
ec_msg->indata[i] = ptr[i + 2];
|
||||
ec_msg->data[i] = ptr[i + 2];
|
||||
}
|
||||
sum &= 0xff;
|
||||
|
||||
|
@ -315,6 +602,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
|||
|
||||
ret = len;
|
||||
exit:
|
||||
kfree(rx_buf);
|
||||
if (ec_msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
|
@ -327,6 +615,10 @@ static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
|
|||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "google,cros-ec-spi-pre-delay", &val);
|
||||
if (!ret)
|
||||
ec_spi->start_of_msg_delay = val;
|
||||
|
||||
ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val);
|
||||
if (!ret)
|
||||
ec_spi->end_of_msg_delay = val;
|
||||
|
@ -361,11 +653,13 @@ static int cros_ec_spi_probe(struct spi_device *spi)
|
|||
ec_dev->priv = ec_spi;
|
||||
ec_dev->irq = spi->irq;
|
||||
ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi;
|
||||
ec_dev->ec_name = ec_spi->spi->modalias;
|
||||
ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi;
|
||||
ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
|
||||
ec_dev->parent = &ec_spi->spi->dev;
|
||||
ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT;
|
||||
ec_dev->dout_size = EC_MSG_BYTES;
|
||||
ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
|
||||
sizeof(struct ec_host_response) +
|
||||
sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->dout_size = sizeof(struct ec_host_request);
|
||||
|
||||
|
||||
err = cros_ec_register(ec_dev);
|
||||
if (err) {
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define DA9052_IRQ_MASK_POS_7 0x40
|
||||
#define DA9052_IRQ_MASK_POS_8 0x80
|
||||
|
||||
static struct regmap_irq da9052_irqs[] = {
|
||||
static const struct regmap_irq da9052_irqs[] = {
|
||||
[DA9052_IRQ_DCIN] = {
|
||||
.reg_offset = 0,
|
||||
.mask = DA9052_IRQ_MASK_POS_1,
|
||||
|
@ -166,7 +166,7 @@ static struct regmap_irq da9052_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip da9052_regmap_irq_chip = {
|
||||
static const struct regmap_irq_chip da9052_regmap_irq_chip = {
|
||||
.name = "da9052_irq",
|
||||
.status_base = DA9052_EVENT_A_REG,
|
||||
.mask_base = DA9052_IRQ_MASK_A_REG,
|
||||
|
|
|
@ -222,7 +222,7 @@ static bool da9055_register_volatile(struct device *dev, unsigned int reg)
|
|||
}
|
||||
}
|
||||
|
||||
static struct regmap_irq da9055_irqs[] = {
|
||||
static const struct regmap_irq da9055_irqs[] = {
|
||||
[DA9055_IRQ_NONKEY] = {
|
||||
.reg_offset = 0,
|
||||
.mask = DA9055_IRQ_NONKEY_MASK,
|
||||
|
@ -245,7 +245,7 @@ static struct regmap_irq da9055_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
struct regmap_config da9055_regmap_config = {
|
||||
const struct regmap_config da9055_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
|
@ -367,7 +367,7 @@ static const struct mfd_cell da9055_devs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip da9055_regmap_irq_chip = {
|
||||
static const struct regmap_irq_chip da9055_regmap_irq_chip = {
|
||||
.name = "da9055_irq",
|
||||
.status_base = DA9055_REG_EVENT_A,
|
||||
.mask_base = DA9055_REG_IRQ_MASK_A,
|
||||
|
|
|
@ -60,6 +60,7 @@ static struct resource da9063_rtc_resources[] = {
|
|||
|
||||
static struct resource da9063_onkey_resources[] = {
|
||||
{
|
||||
.name = "ONKEY",
|
||||
.start = DA9063_IRQ_ONKEY,
|
||||
.end = DA9063_IRQ_ONKEY,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
|
@ -97,6 +98,7 @@ static const struct mfd_cell da9063_devs[] = {
|
|||
.name = DA9063_DRVNAME_ONKEY,
|
||||
.num_resources = ARRAY_SIZE(da9063_onkey_resources),
|
||||
.resources = da9063_onkey_resources,
|
||||
.of_compatible = "dlg,da9063-onkey",
|
||||
},
|
||||
{
|
||||
.name = DA9063_DRVNAME_RTC,
|
||||
|
@ -109,12 +111,64 @@ static const struct mfd_cell da9063_devs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int da9063_clear_fault_log(struct da9063 *da9063)
|
||||
{
|
||||
int ret = 0;
|
||||
int fault_log = 0;
|
||||
|
||||
ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log);
|
||||
if (ret < 0) {
|
||||
dev_err(da9063->dev, "Cannot read FAULT_LOG.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (fault_log) {
|
||||
if (fault_log & DA9063_TWD_ERROR)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_TWD_ERROR\n");
|
||||
if (fault_log & DA9063_POR)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_POR\n");
|
||||
if (fault_log & DA9063_VDD_FAULT)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_VDD_FAULT\n");
|
||||
if (fault_log & DA9063_VDD_START)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_VDD_START\n");
|
||||
if (fault_log & DA9063_TEMP_CRIT)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_TEMP_CRIT\n");
|
||||
if (fault_log & DA9063_KEY_RESET)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_KEY_RESET\n");
|
||||
if (fault_log & DA9063_NSHUTDOWN)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_NSHUTDOWN\n");
|
||||
if (fault_log & DA9063_WAIT_SHUT)
|
||||
dev_dbg(da9063->dev,
|
||||
"Fault log entry detected: DA9063_WAIT_SHUT\n");
|
||||
}
|
||||
|
||||
ret = regmap_write(da9063->regmap,
|
||||
DA9063_REG_FAULT_LOG,
|
||||
fault_log);
|
||||
if (ret < 0)
|
||||
dev_err(da9063->dev,
|
||||
"Cannot reset FAULT_LOG values %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
|
||||
{
|
||||
struct da9063_pdata *pdata = da9063->dev->platform_data;
|
||||
int model, variant_id, variant_code;
|
||||
int ret;
|
||||
|
||||
ret = da9063_clear_fault_log(da9063);
|
||||
if (ret < 0)
|
||||
dev_err(da9063->dev, "Cannot clear fault log\n");
|
||||
|
||||
if (pdata) {
|
||||
da9063->flags = pdata->flags;
|
||||
da9063->irq_base = pdata->irq_base;
|
||||
|
|
|
@ -34,7 +34,7 @@ struct da9063_irq_data {
|
|||
u8 mask;
|
||||
};
|
||||
|
||||
static struct regmap_irq da9063_irqs[] = {
|
||||
static const struct regmap_irq da9063_irqs[] = {
|
||||
/* DA9063 event A register */
|
||||
[DA9063_IRQ_ONKEY] = {
|
||||
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
|
||||
|
@ -153,7 +153,7 @@ static struct regmap_irq da9063_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip da9063_irq_chip = {
|
||||
static const struct regmap_irq_chip da9063_irq_chip = {
|
||||
.name = "da9063-irq",
|
||||
.irqs = da9063_irqs,
|
||||
.num_irqs = DA9063_NUM_IRQ,
|
||||
|
|
|
@ -164,7 +164,7 @@ void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(da9150_bulk_write);
|
||||
|
||||
static struct regmap_irq da9150_irqs[] = {
|
||||
static const struct regmap_irq da9150_irqs[] = {
|
||||
[DA9150_IRQ_VBUS] = {
|
||||
.reg_offset = 0,
|
||||
.mask = DA9150_E_VBUS_MASK,
|
||||
|
@ -251,7 +251,7 @@ static struct regmap_irq da9150_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip da9150_regmap_irq_chip = {
|
||||
static const struct regmap_irq_chip da9150_regmap_irq_chip = {
|
||||
.name = "da9150_irq",
|
||||
.status_base = DA9150_EVENT_E,
|
||||
.mask_base = DA9150_IRQ_MASK_E,
|
||||
|
|
|
@ -2659,7 +2659,7 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops db8500_irq_ops = {
|
||||
static const struct irq_domain_ops db8500_irq_ops = {
|
||||
.map = db8500_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -564,7 +564,8 @@ static int htcpld_core_probe(struct platform_device *pdev)
|
|||
htcpld->chained_irq = res->start;
|
||||
|
||||
/* Setup the chained interrupt handler */
|
||||
flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
|
||||
flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
|
||||
IRQF_ONESHOT;
|
||||
ret = request_threaded_irq(htcpld->chained_irq,
|
||||
NULL, htcpld_handler,
|
||||
flags, pdev->name, htcpld);
|
||||
|
|
|
@ -24,7 +24,7 @@ struct intel_soc_pmic_config {
|
|||
struct mfd_cell *cell_dev;
|
||||
int n_cell_devs;
|
||||
const struct regmap_config *regmap_config;
|
||||
struct regmap_irq_chip *irq_chip;
|
||||
const struct regmap_irq_chip *irq_chip;
|
||||
};
|
||||
|
||||
extern struct intel_soc_pmic_config intel_soc_pmic_config_crc;
|
||||
|
|
|
@ -143,7 +143,7 @@ static const struct regmap_irq crystal_cove_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip crystal_cove_irq_chip = {
|
||||
static const struct regmap_irq_chip crystal_cove_irq_chip = {
|
||||
.name = "Crystal Cove",
|
||||
.irqs = crystal_cove_irqs,
|
||||
.num_irqs = ARRAY_SIZE(crystal_cove_irqs),
|
||||
|
|
|
@ -151,7 +151,7 @@ static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops lp8788_domain_ops = {
|
||||
static const struct irq_domain_ops lp8788_domain_ops = {
|
||||
.map = lp8788_irq_map,
|
||||
};
|
||||
|
||||
|
|
|
@ -934,8 +934,8 @@ gpe0_done:
|
|||
lpc_ich_enable_gpio_space(dev);
|
||||
|
||||
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
|
||||
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
|
||||
1, NULL, 0, NULL);
|
||||
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
|
||||
&lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL);
|
||||
|
||||
gpio_done:
|
||||
if (acpi_conflict)
|
||||
|
@ -1008,8 +1008,8 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
|
|||
}
|
||||
|
||||
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
|
||||
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
|
||||
1, NULL, 0, NULL);
|
||||
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
|
||||
&lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL);
|
||||
|
||||
wdt_done:
|
||||
return ret;
|
||||
|
|
|
@ -658,7 +658,7 @@ static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max8925_irq_domain_ops = {
|
||||
static const struct irq_domain_ops max8925_irq_domain_ops = {
|
||||
.map = max8925_irq_domain_map,
|
||||
.xlate = irq_domain_xlate_onetwocell,
|
||||
};
|
||||
|
|
|
@ -303,7 +303,7 @@ static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max8997_irq_domain_ops = {
|
||||
static const struct irq_domain_ops max8997_irq_domain_ops = {
|
||||
.map = max8997_irq_domain_map,
|
||||
};
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max8998_irq_domain_ops = {
|
||||
static const struct irq_domain_ops max8998_irq_domain_ops = {
|
||||
.map = max8998_irq_domain_map,
|
||||
};
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
|
|||
int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq);
|
||||
|
||||
return devm_request_threaded_irq(mc13xxx->dev, virq, NULL, handler,
|
||||
0, name, dev);
|
||||
IRQF_ONESHOT, name, dev);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_request);
|
||||
|
||||
|
|
|
@ -207,9 +207,11 @@ static int mfd_add_device(struct device *parent, int id,
|
|||
}
|
||||
|
||||
if (!cell->ignore_resource_conflicts) {
|
||||
ret = acpi_check_resource_conflict(&res[r]);
|
||||
if (ret)
|
||||
goto fail_alias;
|
||||
if (has_acpi_companion(&pdev->dev)) {
|
||||
ret = acpi_check_resource_conflict(&res[r]);
|
||||
if (ret)
|
||||
goto fail_alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ static const struct mfd_cell mt6397_devs[] = {
|
|||
}, {
|
||||
.name = "mt6397-clk",
|
||||
.of_compatible = "mediatek,mt6397-clk",
|
||||
}, {
|
||||
.name = "mt6397-pinctrl",
|
||||
.of_compatible = "mediatek,mt6397-pinctrl",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -130,7 +133,7 @@ static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops mt6397_irq_domain_ops = {
|
||||
static const struct irq_domain_ops mt6397_irq_domain_ops = {
|
||||
.map = mt6397_irq_domain_map,
|
||||
};
|
||||
|
||||
|
|
|
@ -777,7 +777,8 @@ static int si476x_core_probe(struct i2c_client *client,
|
|||
rval = devm_request_threaded_irq(&client->dev,
|
||||
client->irq, NULL,
|
||||
si476x_core_interrupt,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
client->name, core);
|
||||
if (rval < 0) {
|
||||
dev_err(&client->dev, "Could not request IRQ %d\n",
|
||||
|
|
|
@ -989,7 +989,7 @@ static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq)
|
|||
irq_set_chip_data(virq, NULL);
|
||||
}
|
||||
|
||||
static struct irq_domain_ops stmpe_irq_ops = {
|
||||
static const struct irq_domain_ops stmpe_irq_ops = {
|
||||
.map = stmpe_irq_map,
|
||||
.unmap = stmpe_irq_unmap,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
|
|
|
@ -233,7 +233,7 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
|
|||
irq_set_chip_data(virq, NULL);
|
||||
}
|
||||
|
||||
static struct irq_domain_ops tc3589x_irq_ops = {
|
||||
static const struct irq_domain_ops tc3589x_irq_ops = {
|
||||
.map = tc3589x_irq_map,
|
||||
.unmap = tc3589x_irq_unmap,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
|
|
|
@ -311,7 +311,7 @@ static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops tps6586x_domain_ops = {
|
||||
static const struct irq_domain_ops tps6586x_domain_ops = {
|
||||
.map = tps6586x_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -674,7 +674,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base)
|
|||
irq_set_handler_data(irq, agent);
|
||||
agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name);
|
||||
status = request_threaded_irq(irq, NULL, handle_twl4030_sih,
|
||||
IRQF_EARLY_RESUME,
|
||||
IRQF_EARLY_RESUME | IRQF_ONESHOT,
|
||||
agent->irq_name ?: sih->name, NULL);
|
||||
|
||||
dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
|
||||
|
|
|
@ -264,7 +264,9 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int twl4030_config_wakeup12_sequence(u8 address)
|
||||
static int
|
||||
twl4030_config_wakeup12_sequence(const struct twl4030_power_data *pdata,
|
||||
u8 address)
|
||||
{
|
||||
int err = 0;
|
||||
u8 data;
|
||||
|
@ -293,13 +295,14 @@ static int twl4030_config_wakeup12_sequence(u8 address)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
|
||||
if (pdata->ac_charger_quirk || machine_is_omap_3430sdp() ||
|
||||
machine_is_omap_ldp()) {
|
||||
/* Disabling AC charger effect on sleep-active transitions */
|
||||
err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data,
|
||||
R_CFG_P1_TRANSITION);
|
||||
if (err)
|
||||
goto out;
|
||||
data &= ~(1<<1);
|
||||
data &= ~STARTON_CHG;
|
||||
err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data,
|
||||
R_CFG_P1_TRANSITION);
|
||||
if (err)
|
||||
|
@ -459,8 +462,9 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int load_twl4030_script(struct twl4030_script *tscript,
|
||||
u8 address)
|
||||
static int load_twl4030_script(const struct twl4030_power_data *pdata,
|
||||
struct twl4030_script *tscript,
|
||||
u8 address)
|
||||
{
|
||||
int err;
|
||||
static int order;
|
||||
|
@ -487,7 +491,7 @@ static int load_twl4030_script(struct twl4030_script *tscript,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
err = twl4030_config_wakeup12_sequence(address);
|
||||
err = twl4030_config_wakeup12_sequence(pdata, address);
|
||||
if (err)
|
||||
goto out;
|
||||
order = 1;
|
||||
|
@ -567,7 +571,7 @@ twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)
|
|||
u8 address = twl4030_start_script_address;
|
||||
|
||||
for (i = 0; i < pdata->num; i++) {
|
||||
err = load_twl4030_script(pdata->scripts[i], address);
|
||||
err = load_twl4030_script(pdata, pdata->scripts[i], address);
|
||||
if (err)
|
||||
return err;
|
||||
address += pdata->scripts[i]->size;
|
||||
|
@ -829,6 +833,21 @@ static struct twl4030_power_data osc_off_idle = {
|
|||
.board_config = osc_off_rconfig,
|
||||
};
|
||||
|
||||
static struct twl4030_power_data omap3_idle_ac_quirk = {
|
||||
.scripts = omap3_idle_scripts,
|
||||
.num = ARRAY_SIZE(omap3_idle_scripts),
|
||||
.resource_config = omap3_idle_rconfig,
|
||||
.ac_charger_quirk = true,
|
||||
};
|
||||
|
||||
static struct twl4030_power_data omap3_idle_ac_quirk_osc_off = {
|
||||
.scripts = omap3_idle_scripts,
|
||||
.num = ARRAY_SIZE(omap3_idle_scripts),
|
||||
.resource_config = omap3_idle_rconfig,
|
||||
.board_config = osc_off_rconfig,
|
||||
.ac_charger_quirk = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id twl4030_power_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,twl4030-power",
|
||||
|
@ -845,6 +864,18 @@ static const struct of_device_id twl4030_power_of_match[] = {
|
|||
.compatible = "ti,twl4030-power-idle-osc-off",
|
||||
.data = &osc_off_idle,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,twl4030-power-omap3-sdp",
|
||||
.data = &omap3_idle_ac_quirk,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,twl4030-power-omap3-ldp",
|
||||
.data = &omap3_idle_ac_quirk_osc_off,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,twl4030-power-omap3-evm",
|
||||
.data = &omap3_idle_ac_quirk,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
|
||||
|
|
|
@ -376,7 +376,7 @@ static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
|
|||
irq_set_chip_data(virq, NULL);
|
||||
}
|
||||
|
||||
static struct irq_domain_ops twl6030_irq_domain_ops = {
|
||||
static const struct irq_domain_ops twl6030_irq_domain_ops = {
|
||||
.map = twl6030_irq_map,
|
||||
.unmap = twl6030_irq_unmap,
|
||||
.xlate = irq_domain_xlate_onetwocell,
|
||||
|
|
|
@ -285,7 +285,8 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
|
|||
|
||||
ret = request_threaded_irq(wm831x_irq(wm831x,
|
||||
WM831X_IRQ_AUXADC_DATA),
|
||||
NULL, wm831x_auxadc_irq, 0,
|
||||
NULL, wm831x_auxadc_irq,
|
||||
IRQF_ONESHOT,
|
||||
"auxadc", wm831x);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
|
||||
|
|
|
@ -564,7 +564,7 @@ static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops wm831x_irq_domain_ops = {
|
||||
static const struct irq_domain_ops wm831x_irq_domain_ops = {
|
||||
.map = wm831x_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -404,7 +404,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
|||
if (wm8350->irq_base) {
|
||||
ret = request_threaded_irq(wm8350->irq_base +
|
||||
WM8350_IRQ_AUXADC_DATARDY,
|
||||
NULL, wm8350_auxadc_irq, 0,
|
||||
NULL, wm8350_auxadc_irq,
|
||||
IRQF_ONESHOT,
|
||||
"auxadc", wm8350);
|
||||
if (ret < 0)
|
||||
dev_warn(wm8350->dev,
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
|
||||
static struct regmap_irq wm8994_irqs[] = {
|
||||
static const struct regmap_irq wm8994_irqs[] = {
|
||||
[WM8994_IRQ_TEMP_SHUT] = {
|
||||
.reg_offset = 1,
|
||||
.mask = WM8994_TEMP_SHUT_EINT,
|
||||
|
@ -128,7 +128,7 @@ static struct regmap_irq wm8994_irqs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip wm8994_irq_chip = {
|
||||
static const struct regmap_irq_chip wm8994_irq_chip = {
|
||||
.name = "wm8994",
|
||||
.irqs = wm8994_irqs,
|
||||
.num_irqs = ARRAY_SIZE(wm8994_irqs),
|
||||
|
@ -184,7 +184,7 @@ static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops wm8994_edge_irq_ops = {
|
||||
static const struct irq_domain_ops wm8994_edge_irq_ops = {
|
||||
.map = wm8994_edge_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ config CHROMEOS_PSTORE
|
|||
|
||||
config CROS_EC_CHARDEV
|
||||
tristate "Chrome OS Embedded Controller userspace device interface"
|
||||
depends on MFD_CROS_EC
|
||||
depends on CROS_EC_PROTO
|
||||
---help---
|
||||
This driver adds support to talk with the ChromeOS EC from userspace.
|
||||
|
||||
|
@ -49,7 +49,7 @@ config CROS_EC_CHARDEV
|
|||
|
||||
config CROS_EC_LPC
|
||||
tristate "ChromeOS Embedded Controller (LPC)"
|
||||
depends on MFD_CROS_EC && (X86 || COMPILE_TEST)
|
||||
depends on MFD_CROS_EC && CROS_EC_PROTO && (X86 || COMPILE_TEST)
|
||||
help
|
||||
If you say Y here, you get support for talking to the ChromeOS EC
|
||||
over an LPC bus. This uses a simple byte-level protocol with a
|
||||
|
@ -59,4 +59,9 @@ config CROS_EC_LPC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called cros_ec_lpc.
|
||||
|
||||
config CROS_EC_PROTO
|
||||
bool
|
||||
help
|
||||
ChromeOS EC communication protocol helpers.
|
||||
|
||||
endif # CHROMEOS_PLATFORMS
|
||||
|
|
|
@ -4,3 +4,4 @@ obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
|
|||
cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o
|
||||
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
|
||||
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
|
||||
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o
|
||||
|
|
|
@ -20,44 +20,59 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "cros_ec_dev.h"
|
||||
|
||||
/* Device variables */
|
||||
#define CROS_MAX_DEV 128
|
||||
static struct class *cros_class;
|
||||
static int ec_major;
|
||||
|
||||
static const struct attribute_group *cros_ec_groups[] = {
|
||||
&cros_ec_attr_group,
|
||||
&cros_ec_lightbar_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct class cros_class = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "chromeos",
|
||||
.dev_groups = cros_ec_groups,
|
||||
};
|
||||
|
||||
/* Basic communication */
|
||||
static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
|
||||
static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
|
||||
{
|
||||
struct ec_response_get_version *resp;
|
||||
static const char * const current_image_name[] = {
|
||||
"unknown", "read-only", "read-write", "invalid",
|
||||
};
|
||||
struct cros_ec_command msg = {
|
||||
.version = 0,
|
||||
.command = EC_CMD_GET_VERSION,
|
||||
.outdata = { 0 },
|
||||
.outsize = 0,
|
||||
.indata = { 0 },
|
||||
.insize = sizeof(*resp),
|
||||
};
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (msg.result != EC_RES_SUCCESS) {
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
|
||||
msg->insize = sizeof(*resp);
|
||||
msg->outsize = 0;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
snprintf(str, maxlen,
|
||||
"%s\nUnknown EC version: EC returned %d\n",
|
||||
CROS_EC_DEV_VERSION, msg.result);
|
||||
return 0;
|
||||
CROS_EC_DEV_VERSION, msg->result);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
resp = (struct ec_response_get_version *)msg.indata;
|
||||
resp = (struct ec_response_get_version *)msg->data;
|
||||
if (resp->current_image >= ARRAY_SIZE(current_image_name))
|
||||
resp->current_image = 3; /* invalid */
|
||||
|
||||
|
@ -65,14 +80,19 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
|
|||
resp->version_string_ro, resp->version_string_rw,
|
||||
current_image_name[resp->current_image]);
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Device file ops */
|
||||
static int ec_device_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
filp->private_data = container_of(inode->i_cdev,
|
||||
struct cros_ec_device, cdev);
|
||||
struct cros_ec_dev *ec = container_of(inode->i_cdev,
|
||||
struct cros_ec_dev, cdev);
|
||||
filp->private_data = ec;
|
||||
nonseekable_open(inode, filp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -84,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp)
|
|||
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
struct cros_ec_device *ec = filp->private_data;
|
||||
struct cros_ec_dev *ec = filp->private_data;
|
||||
char msg[sizeof(struct ec_response_get_version) +
|
||||
sizeof(CROS_EC_DEV_VERSION)];
|
||||
size_t count;
|
||||
|
@ -107,38 +127,53 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
|
|||
}
|
||||
|
||||
/* Ioctls */
|
||||
static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
|
||||
static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
|
||||
{
|
||||
long ret;
|
||||
struct cros_ec_command s_cmd = { };
|
||||
struct cros_ec_command u_cmd;
|
||||
struct cros_ec_command *s_cmd;
|
||||
|
||||
if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
|
||||
if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &s_cmd);
|
||||
s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
|
||||
GFP_KERNEL);
|
||||
if (!s_cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
|
||||
ret = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s_cmd->command += ec->cmd_offset;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
|
||||
/* Only copy data to userland if data was received. */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
|
||||
ret = -EFAULT;
|
||||
exit:
|
||||
kfree(s_cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
|
||||
static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = ec->ec_dev;
|
||||
struct cros_ec_readmem s_mem = { };
|
||||
long num;
|
||||
|
||||
/* Not every platform supports direct reads */
|
||||
if (!ec->cmd_readmem)
|
||||
if (!ec_dev->cmd_readmem)
|
||||
return -ENOTTY;
|
||||
|
||||
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
|
||||
return -EFAULT;
|
||||
|
||||
num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
|
||||
num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
|
||||
s_mem.buffer);
|
||||
if (num <= 0)
|
||||
return num;
|
||||
|
||||
|
@ -151,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
|
|||
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct cros_ec_device *ec = filp->private_data;
|
||||
struct cros_ec_dev *ec = filp->private_data;
|
||||
|
||||
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
|
||||
return -ENOTTY;
|
||||
|
@ -174,45 +209,81 @@ static const struct file_operations fops = {
|
|||
.unlocked_ioctl = ec_device_ioctl,
|
||||
};
|
||||
|
||||
static void __remove(struct device *dev)
|
||||
{
|
||||
struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
|
||||
class_dev);
|
||||
kfree(ec);
|
||||
}
|
||||
|
||||
static int ec_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
int retval = -ENOTTY;
|
||||
dev_t devno = MKDEV(ec_major, 0);
|
||||
int retval = -ENOMEM;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
|
||||
dev_t devno = MKDEV(ec_major, pdev->id);
|
||||
struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
|
||||
|
||||
/* Instantiate it (and remember the EC) */
|
||||
if (!ec)
|
||||
return retval;
|
||||
|
||||
dev_set_drvdata(dev, ec);
|
||||
ec->ec_dev = dev_get_drvdata(dev->parent);
|
||||
ec->dev = dev;
|
||||
ec->cmd_offset = ec_platform->cmd_offset;
|
||||
device_initialize(&ec->class_dev);
|
||||
cdev_init(&ec->cdev, &fops);
|
||||
|
||||
/*
|
||||
* Add the character device
|
||||
* Link cdev to the class device to be sure device is not used
|
||||
* before unbinding it.
|
||||
*/
|
||||
ec->cdev.kobj.parent = &ec->class_dev.kobj;
|
||||
retval = cdev_add(&ec->cdev, devno, 1);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, ": failed to add character device\n");
|
||||
return retval;
|
||||
dev_err(dev, ": failed to add character device\n");
|
||||
goto cdev_add_failed;
|
||||
}
|
||||
|
||||
ec->vdev = device_create(cros_class, NULL, devno, ec,
|
||||
CROS_EC_DEV_NAME);
|
||||
if (IS_ERR(ec->vdev)) {
|
||||
retval = PTR_ERR(ec->vdev);
|
||||
dev_err(&pdev->dev, ": failed to create device\n");
|
||||
cdev_del(&ec->cdev);
|
||||
return retval;
|
||||
/*
|
||||
* Add the class device
|
||||
* Link to the character device for creating the /dev entry
|
||||
* in devtmpfs.
|
||||
*/
|
||||
ec->class_dev.devt = ec->cdev.dev;
|
||||
ec->class_dev.class = &cros_class;
|
||||
ec->class_dev.parent = dev;
|
||||
ec->class_dev.release = __remove;
|
||||
|
||||
retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
|
||||
if (retval) {
|
||||
dev_err(dev, "dev_set_name failed => %d\n", retval);
|
||||
goto set_named_failed;
|
||||
}
|
||||
|
||||
/* Initialize extra interfaces */
|
||||
ec_dev_sysfs_init(ec);
|
||||
ec_dev_lightbar_init(ec);
|
||||
retval = device_add(&ec->class_dev);
|
||||
if (retval) {
|
||||
dev_err(dev, "device_register failed => %d\n", retval);
|
||||
goto dev_reg_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dev_reg_failed:
|
||||
set_named_failed:
|
||||
dev_set_drvdata(dev, NULL);
|
||||
cdev_del(&ec->cdev);
|
||||
cdev_add_failed:
|
||||
kfree(ec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ec_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
ec_dev_lightbar_remove(ec);
|
||||
ec_dev_sysfs_remove(ec);
|
||||
device_destroy(cros_class, MKDEV(ec_major, 0));
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
|
||||
cdev_del(&ec->cdev);
|
||||
device_unregister(&ec->class_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -229,10 +300,10 @@ static int __init cros_ec_dev_init(void)
|
|||
int ret;
|
||||
dev_t dev = 0;
|
||||
|
||||
cros_class = class_create(THIS_MODULE, "chromeos");
|
||||
if (IS_ERR(cros_class)) {
|
||||
ret = class_register(&cros_class);
|
||||
if (ret) {
|
||||
pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
|
||||
return PTR_ERR(cros_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get a range of minor numbers (starting with 0) to work with */
|
||||
|
@ -254,7 +325,7 @@ static int __init cros_ec_dev_init(void)
|
|||
failed_devreg:
|
||||
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
|
||||
failed_chrdevreg:
|
||||
class_destroy(cros_class);
|
||||
class_unregister(&cros_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -262,7 +333,7 @@ static void __exit cros_ec_dev_exit(void)
|
|||
{
|
||||
platform_driver_unregister(&cros_ec_dev_driver);
|
||||
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
|
||||
class_destroy(cros_class);
|
||||
class_unregister(&cros_class);
|
||||
}
|
||||
|
||||
module_init(cros_ec_dev_init);
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
|
||||
#define CROS_EC_DEV_NAME "cros_ec"
|
||||
#define CROS_EC_DEV_VERSION "1.0.0"
|
||||
|
||||
/*
|
||||
|
@ -44,10 +43,4 @@ struct cros_ec_readmem {
|
|||
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
|
||||
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
|
||||
|
||||
void ec_dev_sysfs_init(struct cros_ec_device *);
|
||||
void ec_dev_sysfs_remove(struct cros_ec_device *);
|
||||
|
||||
void ec_dev_lightbar_init(struct cros_ec_device *);
|
||||
void ec_dev_lightbar_remove(struct cros_ec_device *);
|
||||
|
||||
#endif /* _CROS_EC_DEV_H_ */
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "cros_ec_dev.h"
|
||||
|
||||
|
@ -91,55 +92,81 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define INIT_MSG(P, R) { \
|
||||
.command = EC_CMD_LIGHTBAR_CMD, \
|
||||
.outsize = sizeof(*P), \
|
||||
.insize = sizeof(*R), \
|
||||
}
|
||||
static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
|
||||
{
|
||||
struct cros_ec_command *msg;
|
||||
int len;
|
||||
|
||||
static int get_lightbar_version(struct cros_ec_device *ec,
|
||||
len = max(sizeof(struct ec_params_lightbar),
|
||||
sizeof(struct ec_response_lightbar));
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
|
||||
msg->outsize = sizeof(struct ec_params_lightbar);
|
||||
msg->insize = sizeof(struct ec_response_lightbar);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int get_lightbar_version(struct cros_ec_dev *ec,
|
||||
uint32_t *ver_ptr, uint32_t *flg_ptr)
|
||||
{
|
||||
struct ec_params_lightbar *param;
|
||||
struct ec_response_lightbar *resp;
|
||||
struct cros_ec_command msg = INIT_MSG(param, resp);
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
param = (struct ec_params_lightbar *)msg.outdata;
|
||||
param->cmd = LIGHTBAR_CMD_VERSION;
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
if (ret < 0)
|
||||
msg = alloc_lightbar_cmd_msg(ec);
|
||||
if (!msg)
|
||||
return 0;
|
||||
|
||||
switch (msg.result) {
|
||||
param = (struct ec_params_lightbar *)msg->data;
|
||||
param->cmd = LIGHTBAR_CMD_VERSION;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0) {
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (msg->result) {
|
||||
case EC_RES_INVALID_PARAM:
|
||||
/* Pixel had no version command. */
|
||||
if (ver_ptr)
|
||||
*ver_ptr = 0;
|
||||
if (flg_ptr)
|
||||
*flg_ptr = 0;
|
||||
return 1;
|
||||
ret = 1;
|
||||
goto exit;
|
||||
|
||||
case EC_RES_SUCCESS:
|
||||
resp = (struct ec_response_lightbar *)msg.indata;
|
||||
resp = (struct ec_response_lightbar *)msg->data;
|
||||
|
||||
/* Future devices w/lightbars should implement this command */
|
||||
if (ver_ptr)
|
||||
*ver_ptr = resp->version.num;
|
||||
if (flg_ptr)
|
||||
*flg_ptr = resp->version.flags;
|
||||
return 1;
|
||||
ret = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
|
||||
return 0;
|
||||
ret = 0;
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
uint32_t version, flags;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
uint32_t version = 0, flags = 0;
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
int ret;
|
||||
|
||||
ret = lb_throttle();
|
||||
|
@ -158,30 +185,39 @@ static ssize_t brightness_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct ec_params_lightbar *param;
|
||||
struct ec_response_lightbar *resp;
|
||||
struct cros_ec_command msg = INIT_MSG(param, resp);
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
if (kstrtouint(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
param = (struct ec_params_lightbar *)msg.outdata;
|
||||
param->cmd = LIGHTBAR_CMD_BRIGHTNESS;
|
||||
param->brightness.num = val;
|
||||
msg = alloc_lightbar_cmd_msg(ec);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
param = (struct ec_params_lightbar *)msg->data;
|
||||
param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
|
||||
param->set_brightness.num = val;
|
||||
ret = lb_throttle();
|
||||
if (ret)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
return -EINVAL;
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return count;
|
||||
ret = count;
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -196,12 +232,16 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct ec_params_lightbar *param;
|
||||
struct ec_response_lightbar *resp;
|
||||
struct cros_ec_command msg = INIT_MSG(param, resp);
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_command *msg;
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
unsigned int val[4];
|
||||
int ret, i = 0, j = 0, ok = 0;
|
||||
|
||||
msg = alloc_lightbar_cmd_msg(ec);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
do {
|
||||
/* Skip any whitespace */
|
||||
while (*buf && isspace(*buf))
|
||||
|
@ -215,12 +255,12 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
|
|||
return -EINVAL;
|
||||
|
||||
if (i == 4) {
|
||||
param = (struct ec_params_lightbar *)msg.outdata;
|
||||
param->cmd = LIGHTBAR_CMD_RGB;
|
||||
param->rgb.led = val[0];
|
||||
param->rgb.red = val[1];
|
||||
param->rgb.green = val[2];
|
||||
param->rgb.blue = val[3];
|
||||
param = (struct ec_params_lightbar *)msg->data;
|
||||
param->cmd = LIGHTBAR_CMD_SET_RGB;
|
||||
param->set_rgb.led = val[0];
|
||||
param->set_rgb.red = val[1];
|
||||
param->set_rgb.green = val[2];
|
||||
param->set_rgb.blue = val[3];
|
||||
/*
|
||||
* Throttle only the first of every four transactions,
|
||||
* so that the user can update all four LEDs at once.
|
||||
|
@ -231,12 +271,14 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
return -EINVAL;
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
ok = 1;
|
||||
|
@ -248,6 +290,8 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
} while (*buf);
|
||||
|
||||
exit:
|
||||
kfree(msg);
|
||||
return (ok && i == 0) ? count : -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -261,41 +305,56 @@ static ssize_t sequence_show(struct device *dev,
|
|||
{
|
||||
struct ec_params_lightbar *param;
|
||||
struct ec_response_lightbar *resp;
|
||||
struct cros_ec_command msg = INIT_MSG(param, resp);
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
param = (struct ec_params_lightbar *)msg.outdata;
|
||||
msg = alloc_lightbar_cmd_msg(ec);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
param = (struct ec_params_lightbar *)msg->data;
|
||||
param->cmd = LIGHTBAR_CMD_GET_SEQ;
|
||||
ret = lb_throttle();
|
||||
if (ret)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto exit;
|
||||
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg.result);
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
ret = scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg->result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
resp = (struct ec_response_lightbar *)msg.indata;
|
||||
resp = (struct ec_response_lightbar *)msg->data;
|
||||
if (resp->get_seq.num >= ARRAY_SIZE(seqname))
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
|
||||
ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
|
||||
else
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n",
|
||||
seqname[resp->get_seq.num]);
|
||||
ret = scnprintf(buf, PAGE_SIZE, "%s\n",
|
||||
seqname[resp->get_seq.num]);
|
||||
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ec_params_lightbar *param;
|
||||
struct ec_response_lightbar *resp;
|
||||
struct cros_ec_command msg = INIT_MSG(param, resp);
|
||||
struct cros_ec_command *msg;
|
||||
unsigned int num;
|
||||
int ret, len;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
msg = alloc_lightbar_cmd_msg(ec);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
for (len = 0; len < count; len++)
|
||||
if (!isalnum(buf[len]))
|
||||
|
@ -311,18 +370,18 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
param = (struct ec_params_lightbar *)msg.outdata;
|
||||
param = (struct ec_params_lightbar *)msg->data;
|
||||
param->cmd = LIGHTBAR_CMD_SEQ;
|
||||
param->seq.num = num;
|
||||
ret = lb_throttle();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
if (msg->result != EC_RES_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
|
@ -343,25 +402,27 @@ static struct attribute *__lb_cmds_attrs[] = {
|
|||
&dev_attr_sequence.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group lb_cmds_attr_group = {
|
||||
.name = "lightbar",
|
||||
.attrs = __lb_cmds_attrs,
|
||||
};
|
||||
|
||||
void ec_dev_lightbar_init(struct cros_ec_device *ec)
|
||||
static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
struct platform_device *pdev = container_of(ec->dev,
|
||||
struct platform_device, dev);
|
||||
if (pdev->id != 0)
|
||||
return 0;
|
||||
|
||||
/* Only instantiate this stuff if the EC has a lightbar */
|
||||
if (!get_lightbar_version(ec, NULL, NULL))
|
||||
return;
|
||||
|
||||
ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group);
|
||||
if (ret)
|
||||
pr_warn("sysfs_create_group() failed: %d\n", ret);
|
||||
if (get_lightbar_version(ec, NULL, NULL))
|
||||
return a->mode;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ec_dev_lightbar_remove(struct cros_ec_device *ec)
|
||||
{
|
||||
sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group);
|
||||
}
|
||||
struct attribute_group cros_ec_lightbar_attr_group = {
|
||||
.name = "lightbar",
|
||||
.attrs = __lb_cmds_attrs,
|
||||
.is_visible = cros_ec_lightbar_attrs_are_visible,
|
||||
};
|
||||
|
|
|
@ -46,6 +46,77 @@ static int ec_response_timed_out(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct ec_host_request *request;
|
||||
struct ec_host_response response;
|
||||
u8 sum = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
u8 *dout;
|
||||
|
||||
ret = cros_ec_prepare_tx(ec, msg);
|
||||
|
||||
/* Write buffer */
|
||||
for (i = 0; i < ret; i++)
|
||||
outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i);
|
||||
|
||||
request = (struct ec_host_request *)ec->dout;
|
||||
|
||||
/* Here we go */
|
||||
outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
|
||||
|
||||
if (ec_response_timed_out()) {
|
||||
dev_warn(ec->dev, "EC responsed timed out\n");
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check result */
|
||||
msg->result = inb(EC_LPC_ADDR_HOST_DATA);
|
||||
ret = cros_ec_check_result(ec, msg);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
/* Read back response */
|
||||
dout = (u8 *)&response;
|
||||
for (i = 0; i < sizeof(response); i++) {
|
||||
dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i);
|
||||
sum += dout[i];
|
||||
}
|
||||
|
||||
msg->result = response.result;
|
||||
|
||||
if (response.data_len > msg->insize) {
|
||||
dev_err(ec->dev,
|
||||
"packet too long (%d bytes, expected %d)",
|
||||
response.data_len, msg->insize);
|
||||
ret = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Read response and process checksum */
|
||||
for (i = 0; i < response.data_len; i++) {
|
||||
msg->data[i] =
|
||||
inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i);
|
||||
sum += msg->data[i];
|
||||
}
|
||||
|
||||
if (sum) {
|
||||
dev_err(ec->dev,
|
||||
"bad packet checksum %02x\n",
|
||||
response.checksum);
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return actual amount of data received */
|
||||
ret = response.data_len;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
|
@ -73,8 +144,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
|
|||
|
||||
/* Copy data and update checksum */
|
||||
for (i = 0; i < msg->outsize; i++) {
|
||||
outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
|
||||
csum += msg->outdata[i];
|
||||
outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i);
|
||||
csum += msg->data[i];
|
||||
}
|
||||
|
||||
/* Finalize checksum and write args */
|
||||
|
@ -129,8 +200,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
|
|||
|
||||
/* Read response and update checksum */
|
||||
for (i = 0; i < args.data_size; i++) {
|
||||
msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
|
||||
csum += msg->indata[i];
|
||||
msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
|
||||
csum += msg->data[i];
|
||||
}
|
||||
|
||||
/* Verify checksum */
|
||||
|
@ -212,11 +283,13 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, ec_dev);
|
||||
ec_dev->dev = dev;
|
||||
ec_dev->ec_name = pdev->name;
|
||||
ec_dev->phys_name = dev_name(dev);
|
||||
ec_dev->parent = dev;
|
||||
ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
|
||||
ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc;
|
||||
ec_dev->cmd_readmem = cros_ec_lpc_readmem;
|
||||
ec_dev->din_size = sizeof(struct ec_host_response) +
|
||||
sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->dout_size = sizeof(struct ec_host_request);
|
||||
|
||||
ret = cros_ec_register(ec_dev);
|
||||
if (ret) {
|
||||
|
|
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* ChromeOS EC communication protocol helper functions
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define EC_COMMAND_RETRIES 50
|
||||
|
||||
static int prepare_packet(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct ec_host_request *request;
|
||||
u8 *out;
|
||||
int i;
|
||||
u8 csum = 0;
|
||||
|
||||
BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
|
||||
BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
|
||||
|
||||
out = ec_dev->dout;
|
||||
request = (struct ec_host_request *)out;
|
||||
request->struct_version = EC_HOST_REQUEST_VERSION;
|
||||
request->checksum = 0;
|
||||
request->command = msg->command;
|
||||
request->command_version = msg->version;
|
||||
request->reserved = 0;
|
||||
request->data_len = msg->outsize;
|
||||
|
||||
for (i = 0; i < sizeof(*request); i++)
|
||||
csum += out[i];
|
||||
|
||||
/* Copy data and update checksum */
|
||||
memcpy(out + sizeof(*request), msg->data, msg->outsize);
|
||||
for (i = 0; i < msg->outsize; i++)
|
||||
csum += msg->data[i];
|
||||
|
||||
request->checksum = -csum;
|
||||
|
||||
return sizeof(*request) + msg->outsize;
|
||||
}
|
||||
|
||||
static int send_command(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ec_dev->proto_version > 2)
|
||||
ret = ec_dev->pkt_xfer(ec_dev, msg);
|
||||
else
|
||||
ret = ec_dev->cmd_xfer(ec_dev, msg);
|
||||
|
||||
if (msg->result == EC_RES_IN_PROGRESS) {
|
||||
int i;
|
||||
struct cros_ec_command *status_msg;
|
||||
struct ec_response_get_comms_status *status;
|
||||
|
||||
status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
|
||||
GFP_KERNEL);
|
||||
if (!status_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
status_msg->version = 0;
|
||||
status_msg->command = EC_CMD_GET_COMMS_STATUS;
|
||||
status_msg->insize = sizeof(*status);
|
||||
status_msg->outsize = 0;
|
||||
|
||||
/*
|
||||
* Query the EC's status until it's no longer busy or
|
||||
* we encounter an error.
|
||||
*/
|
||||
for (i = 0; i < EC_COMMAND_RETRIES; i++) {
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
ret = ec_dev->cmd_xfer(ec_dev, status_msg);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
msg->result = status_msg->result;
|
||||
if (status_msg->result != EC_RES_SUCCESS)
|
||||
break;
|
||||
|
||||
status = (struct ec_response_get_comms_status *)
|
||||
status_msg->data;
|
||||
if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(status_msg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
u8 *out;
|
||||
u8 csum;
|
||||
int i;
|
||||
|
||||
if (ec_dev->proto_version > 2)
|
||||
return prepare_packet(ec_dev, msg);
|
||||
|
||||
BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
|
||||
out = ec_dev->dout;
|
||||
out[0] = EC_CMD_VERSION0 + msg->version;
|
||||
out[1] = msg->command;
|
||||
out[2] = msg->outsize;
|
||||
csum = out[0] + out[1] + out[2];
|
||||
for (i = 0; i < msg->outsize; i++)
|
||||
csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
|
||||
out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
|
||||
|
||||
return EC_MSG_TX_PROTO_BYTES + msg->outsize;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_prepare_tx);
|
||||
|
||||
int cros_ec_check_result(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
switch (msg->result) {
|
||||
case EC_RES_SUCCESS:
|
||||
return 0;
|
||||
case EC_RES_IN_PROGRESS:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
|
||||
msg->command);
|
||||
return -EAGAIN;
|
||||
default:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
|
||||
msg->command, msg->result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_check_result);
|
||||
|
||||
static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
|
||||
int devidx,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
/*
|
||||
* Try using v3+ to query for supported protocols. If this
|
||||
* command fails, fall back to v2. Returns the highest protocol
|
||||
* supported by the EC.
|
||||
* Also sets the max request/response/passthru size.
|
||||
*/
|
||||
int ret;
|
||||
|
||||
if (!ec_dev->pkt_xfer)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
|
||||
msg->insize = sizeof(struct ec_response_get_protocol_info);
|
||||
|
||||
ret = send_command(ec_dev, msg);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_dbg(ec_dev->dev,
|
||||
"failed to check for EC[%d] protocol version: %d\n",
|
||||
devidx, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
|
||||
return -ENODEV;
|
||||
else if (msg->result != EC_RES_SUCCESS)
|
||||
return msg->result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct cros_ec_command *msg;
|
||||
struct ec_params_hello *hello_params;
|
||||
struct ec_response_hello *hello_response;
|
||||
int ret;
|
||||
int len = max(sizeof(*hello_params), sizeof(*hello_response));
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_HELLO;
|
||||
hello_params = (struct ec_params_hello *)msg->data;
|
||||
msg->outsize = sizeof(*hello_params);
|
||||
hello_response = (struct ec_response_hello *)msg->data;
|
||||
msg->insize = sizeof(*hello_response);
|
||||
|
||||
hello_params->in_data = 0xa0b0c0d0;
|
||||
|
||||
ret = send_command(ec_dev, msg);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_dbg(ec_dev->dev,
|
||||
"EC failed to respond to v2 hello: %d\n",
|
||||
ret);
|
||||
goto exit;
|
||||
} else if (msg->result != EC_RES_SUCCESS) {
|
||||
dev_err(ec_dev->dev,
|
||||
"EC responded to v2 hello with error: %d\n",
|
||||
msg->result);
|
||||
ret = msg->result;
|
||||
goto exit;
|
||||
} else if (hello_response->out_data != 0xa1b2c3d4) {
|
||||
dev_err(ec_dev->dev,
|
||||
"EC responded to v2 hello with bad result: %u\n",
|
||||
hello_response->out_data);
|
||||
ret = -EBADMSG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cros_ec_query_all(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct device *dev = ec_dev->dev;
|
||||
struct cros_ec_command *proto_msg;
|
||||
struct ec_response_get_protocol_info *proto_info;
|
||||
int ret;
|
||||
|
||||
proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
|
||||
GFP_KERNEL);
|
||||
if (!proto_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* First try sending with proto v3. */
|
||||
ec_dev->proto_version = 3;
|
||||
ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
|
||||
|
||||
if (ret == 0) {
|
||||
proto_info = (struct ec_response_get_protocol_info *)
|
||||
proto_msg->data;
|
||||
ec_dev->max_request = proto_info->max_request_packet_size -
|
||||
sizeof(struct ec_host_request);
|
||||
ec_dev->max_response = proto_info->max_response_packet_size -
|
||||
sizeof(struct ec_host_response);
|
||||
ec_dev->proto_version =
|
||||
min(EC_HOST_REQUEST_VERSION,
|
||||
fls(proto_info->protocol_versions) - 1);
|
||||
dev_dbg(ec_dev->dev,
|
||||
"using proto v%u\n",
|
||||
ec_dev->proto_version);
|
||||
|
||||
ec_dev->din_size = ec_dev->max_response +
|
||||
sizeof(struct ec_host_response) +
|
||||
EC_MAX_RESPONSE_OVERHEAD;
|
||||
ec_dev->dout_size = ec_dev->max_request +
|
||||
sizeof(struct ec_host_request) +
|
||||
EC_MAX_REQUEST_OVERHEAD;
|
||||
|
||||
/*
|
||||
* Check for PD
|
||||
*/
|
||||
ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
|
||||
|
||||
if (ret) {
|
||||
dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
|
||||
ec_dev->max_passthru = 0;
|
||||
} else {
|
||||
dev_dbg(ec_dev->dev, "found PD chip\n");
|
||||
ec_dev->max_passthru =
|
||||
proto_info->max_request_packet_size -
|
||||
sizeof(struct ec_host_request);
|
||||
}
|
||||
} else {
|
||||
/* Try querying with a v2 hello message. */
|
||||
ec_dev->proto_version = 2;
|
||||
ret = cros_ec_host_command_proto_query_v2(ec_dev);
|
||||
|
||||
if (ret == 0) {
|
||||
/* V2 hello succeeded. */
|
||||
dev_dbg(ec_dev->dev, "falling back to proto v2\n");
|
||||
|
||||
ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
|
||||
ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
|
||||
ec_dev->max_passthru = 0;
|
||||
ec_dev->pkt_xfer = NULL;
|
||||
ec_dev->din_size = EC_MSG_BYTES;
|
||||
ec_dev->dout_size = EC_MSG_BYTES;
|
||||
} else {
|
||||
/*
|
||||
* It's possible for a test to occur too early when
|
||||
* the EC isn't listening. If this happens, we'll
|
||||
* test later when the first command is run.
|
||||
*/
|
||||
ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
|
||||
dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
devm_kfree(dev, ec_dev->din);
|
||||
devm_kfree(dev, ec_dev->dout);
|
||||
|
||||
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
|
||||
if (!ec_dev->din) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
|
||||
if (!ec_dev->dout) {
|
||||
devm_kfree(dev, ec_dev->din);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
kfree(proto_msg);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_query_all);
|
||||
|
||||
int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ec_dev->lock);
|
||||
if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
|
||||
ret = cros_ec_query_all(ec_dev);
|
||||
if (ret) {
|
||||
dev_err(ec_dev->dev,
|
||||
"EC version unknown and query failed; aborting command\n");
|
||||
mutex_unlock(&ec_dev->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->insize > ec_dev->max_response) {
|
||||
dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
|
||||
msg->insize = ec_dev->max_response;
|
||||
}
|
||||
|
||||
if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
|
||||
if (msg->outsize > ec_dev->max_request) {
|
||||
dev_err(ec_dev->dev,
|
||||
"request of size %u is too big (max: %u)\n",
|
||||
msg->outsize,
|
||||
ec_dev->max_request);
|
||||
mutex_unlock(&ec_dev->lock);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
} else {
|
||||
if (msg->outsize > ec_dev->max_passthru) {
|
||||
dev_err(ec_dev->dev,
|
||||
"passthru rq of size %u is too big (max: %u)\n",
|
||||
msg->outsize,
|
||||
ec_dev->max_passthru);
|
||||
mutex_unlock(&ec_dev->lock);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
}
|
||||
ret = send_command(ec_dev, msg);
|
||||
mutex_unlock(&ec_dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(cros_ec_cmd_xfer);
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -66,13 +67,19 @@ static ssize_t store_ec_reboot(struct device *dev,
|
|||
{"hibernate", EC_REBOOT_HIBERNATE, 0},
|
||||
{"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
|
||||
};
|
||||
struct cros_ec_command msg = { 0 };
|
||||
struct ec_params_reboot_ec *param =
|
||||
(struct ec_params_reboot_ec *)msg.outdata;
|
||||
struct cros_ec_command *msg;
|
||||
struct ec_params_reboot_ec *param;
|
||||
int got_cmd = 0, offset = 0;
|
||||
int i;
|
||||
int ret;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
param = (struct ec_params_reboot_ec *)msg->data;
|
||||
|
||||
param->flags = 0;
|
||||
while (1) {
|
||||
|
@ -100,19 +107,26 @@ static ssize_t store_ec_reboot(struct device *dev,
|
|||
offset++;
|
||||
}
|
||||
|
||||
if (!got_cmd)
|
||||
return -EINVAL;
|
||||
|
||||
msg.command = EC_CMD_REBOOT_EC;
|
||||
msg.outsize = sizeof(param);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (msg.result != EC_RES_SUCCESS) {
|
||||
dev_dbg(ec->dev, "EC result %d\n", msg.result);
|
||||
return -EINVAL;
|
||||
if (!got_cmd) {
|
||||
count = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
|
||||
msg->outsize = sizeof(*param);
|
||||
msg->insize = 0;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0) {
|
||||
count = ret;
|
||||
goto exit;
|
||||
}
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
dev_dbg(ec->dev, "EC result %d\n", msg->result);
|
||||
count = -EINVAL;
|
||||
}
|
||||
exit:
|
||||
kfree(msg);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -123,22 +137,33 @@ static ssize_t show_ec_version(struct device *dev,
|
|||
struct ec_response_get_version *r_ver;
|
||||
struct ec_response_get_chip_info *r_chip;
|
||||
struct ec_response_board_version *r_board;
|
||||
struct cros_ec_command msg = { 0 };
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
int count = 0;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get versions. RW may change. */
|
||||
msg.command = EC_CMD_GET_VERSION;
|
||||
msg.insize = sizeof(*r_ver);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg.result);
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
|
||||
msg->insize = sizeof(*r_ver);
|
||||
msg->outsize = 0;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0) {
|
||||
count = ret;
|
||||
goto exit;
|
||||
}
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
count = scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg->result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
r_ver = (struct ec_response_get_version *)msg.indata;
|
||||
r_ver = (struct ec_response_get_version *)msg->data;
|
||||
/* Strings should be null-terminated, but let's be sure. */
|
||||
r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
|
||||
r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
|
||||
|
@ -152,33 +177,33 @@ static ssize_t show_ec_version(struct device *dev,
|
|||
image_names[r_ver->current_image] : "?"));
|
||||
|
||||
/* Get build info. */
|
||||
msg.command = EC_CMD_GET_BUILD_INFO;
|
||||
msg.insize = sizeof(msg.indata);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
|
||||
msg->insize = EC_HOST_PARAM_SIZE;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Build info: XFER ERROR %d\n", ret);
|
||||
else if (msg.result != EC_RES_SUCCESS)
|
||||
else if (msg->result != EC_RES_SUCCESS)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Build info: EC error %d\n", msg.result);
|
||||
"Build info: EC error %d\n", msg->result);
|
||||
else {
|
||||
msg.indata[sizeof(msg.indata) - 1] = '\0';
|
||||
msg->data[sizeof(msg->data) - 1] = '\0';
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Build info: %s\n", msg.indata);
|
||||
"Build info: %s\n", msg->data);
|
||||
}
|
||||
|
||||
/* Get chip info. */
|
||||
msg.command = EC_CMD_GET_CHIP_INFO;
|
||||
msg.insize = sizeof(*r_chip);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
|
||||
msg->insize = sizeof(*r_chip);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Chip info: XFER ERROR %d\n", ret);
|
||||
else if (msg.result != EC_RES_SUCCESS)
|
||||
else if (msg->result != EC_RES_SUCCESS)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Chip info: EC error %d\n", msg.result);
|
||||
"Chip info: EC error %d\n", msg->result);
|
||||
else {
|
||||
r_chip = (struct ec_response_get_chip_info *)msg.indata;
|
||||
r_chip = (struct ec_response_get_chip_info *)msg->data;
|
||||
|
||||
r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
|
||||
r_chip->name[sizeof(r_chip->name) - 1] = '\0';
|
||||
|
@ -192,23 +217,25 @@ static ssize_t show_ec_version(struct device *dev,
|
|||
}
|
||||
|
||||
/* Get board version */
|
||||
msg.command = EC_CMD_GET_BOARD_VERSION;
|
||||
msg.insize = sizeof(*r_board);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
|
||||
msg->insize = sizeof(*r_board);
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Board version: XFER ERROR %d\n", ret);
|
||||
else if (msg.result != EC_RES_SUCCESS)
|
||||
else if (msg->result != EC_RES_SUCCESS)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Board version: EC error %d\n", msg.result);
|
||||
"Board version: EC error %d\n", msg->result);
|
||||
else {
|
||||
r_board = (struct ec_response_board_version *)msg.indata;
|
||||
r_board = (struct ec_response_board_version *)msg->data;
|
||||
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count,
|
||||
"Board version: %d\n",
|
||||
r_board->board_version);
|
||||
}
|
||||
|
||||
exit:
|
||||
kfree(msg);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -216,27 +243,39 @@ static ssize_t show_ec_flashinfo(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ec_response_flash_info *resp;
|
||||
struct cros_ec_command msg = { 0 };
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev);
|
||||
struct cros_ec_dev *ec = container_of(dev,
|
||||
struct cros_ec_dev, class_dev);
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* The flash info shouldn't ever change, but ask each time anyway. */
|
||||
msg.command = EC_CMD_FLASH_INFO;
|
||||
msg.insize = sizeof(*resp);
|
||||
ret = cros_ec_cmd_xfer(ec, &msg);
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
|
||||
msg->insize = sizeof(*resp);
|
||||
msg->outsize = 0;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (msg.result != EC_RES_SUCCESS)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg.result);
|
||||
goto exit;
|
||||
if (msg->result != EC_RES_SUCCESS) {
|
||||
ret = scnprintf(buf, PAGE_SIZE,
|
||||
"ERROR: EC returned %d\n", msg->result);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
resp = (struct ec_response_flash_info *)msg.indata;
|
||||
resp = (struct ec_response_flash_info *)msg->data;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"FlashSize %d\nWriteSize %d\n"
|
||||
"EraseSize %d\nProtectSize %d\n",
|
||||
resp->flash_size, resp->write_block_size,
|
||||
resp->erase_block_size, resp->protect_block_size);
|
||||
ret = scnprintf(buf, PAGE_SIZE,
|
||||
"FlashSize %d\nWriteSize %d\n"
|
||||
"EraseSize %d\nProtectSize %d\n",
|
||||
resp->flash_size, resp->write_block_size,
|
||||
resp->erase_block_size, resp->protect_block_size);
|
||||
exit:
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
@ -252,20 +291,7 @@ static struct attribute *__ec_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ec_attr_group = {
|
||||
struct attribute_group cros_ec_attr_group = {
|
||||
.attrs = __ec_attrs,
|
||||
};
|
||||
|
||||
void ec_dev_sysfs_init(struct cros_ec_device *ec)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
|
||||
if (error)
|
||||
pr_warn("failed to create group: %d\n", error);
|
||||
}
|
||||
|
||||
void ec_dev_sysfs_remove(struct cros_ec_device *ec)
|
||||
{
|
||||
sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
|
||||
}
|
||||
|
|
|
@ -27,20 +27,24 @@
|
|||
#define AXP20X_IO_ENABLED 0x03
|
||||
#define AXP20X_IO_DISABLED 0x07
|
||||
|
||||
#define AXP22X_IO_ENABLED 0x04
|
||||
#define AXP22X_IO_DISABLED 0x03
|
||||
|
||||
#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
|
||||
#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
|
||||
#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x)
|
||||
|
||||
#define AXP20X_FREQ_DCDC_MASK 0x0f
|
||||
|
||||
#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
|
||||
_ereg, _emask, _enable_val, _disable_val) \
|
||||
[AXP20X_##_id] = { \
|
||||
#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
|
||||
_vmask, _ereg, _emask, _enable_val, _disable_val) \
|
||||
[_family##_##_id] = { \
|
||||
.name = #_id, \
|
||||
.supply_name = (_supply), \
|
||||
.of_match = of_match_ptr(_match), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = AXP20X_##_id, \
|
||||
.id = _family##_##_id, \
|
||||
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (_min) * 1000, \
|
||||
|
@ -54,15 +58,15 @@
|
|||
.ops = &axp20x_ops, \
|
||||
}
|
||||
|
||||
#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
|
||||
_ereg, _emask) \
|
||||
[AXP20X_##_id] = { \
|
||||
#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
|
||||
_vmask, _ereg, _emask) \
|
||||
[_family##_##_id] = { \
|
||||
.name = #_id, \
|
||||
.supply_name = (_supply), \
|
||||
.of_match = of_match_ptr(_match), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = AXP20X_##_id, \
|
||||
.id = _family##_##_id, \
|
||||
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (_min) * 1000, \
|
||||
|
@ -74,29 +78,49 @@
|
|||
.ops = &axp20x_ops, \
|
||||
}
|
||||
|
||||
#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \
|
||||
[AXP20X_##_id] = { \
|
||||
#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
|
||||
_vmask, _ereg, _emask) \
|
||||
[_family##_##_id] = { \
|
||||
.name = #_id, \
|
||||
.supply_name = (_supply), \
|
||||
.of_match = of_match_ptr(_match), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = AXP20X_##_id, \
|
||||
.id = _family##_##_id, \
|
||||
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (_min) * 1000, \
|
||||
.uV_step = (_step) * 1000, \
|
||||
.vsel_reg = (_vreg), \
|
||||
.vsel_mask = (_vmask), \
|
||||
.enable_reg = (_ereg), \
|
||||
.enable_mask = (_emask), \
|
||||
.ops = &axp20x_ops_sw, \
|
||||
}
|
||||
|
||||
#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
|
||||
[_family##_##_id] = { \
|
||||
.name = #_id, \
|
||||
.supply_name = (_supply), \
|
||||
.of_match = of_match_ptr(_match), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = _family##_##_id, \
|
||||
.n_voltages = 1, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = (_volt) * 1000, \
|
||||
.ops = &axp20x_ops_fixed \
|
||||
}
|
||||
|
||||
#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \
|
||||
_emask) \
|
||||
[AXP20X_##_id] = { \
|
||||
#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask, \
|
||||
_ereg, _emask) \
|
||||
[_family##_##_id] = { \
|
||||
.name = #_id, \
|
||||
.supply_name = (_supply), \
|
||||
.of_match = of_match_ptr(_match), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = AXP20X_##_id, \
|
||||
.id = _family##_##_id, \
|
||||
.n_voltages = ARRAY_SIZE(_table), \
|
||||
.owner = THIS_MODULE, \
|
||||
.vsel_reg = (_vreg), \
|
||||
|
@ -135,38 +159,118 @@ static struct regulator_ops axp20x_ops = {
|
|||
.is_enabled = regulator_is_enabled_regmap,
|
||||
};
|
||||
|
||||
static struct regulator_ops axp20x_ops_sw = {
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_desc axp20x_regulators[] = {
|
||||
AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT,
|
||||
0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
|
||||
AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT,
|
||||
0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
|
||||
AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300),
|
||||
AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
|
||||
AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
|
||||
AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT,
|
||||
0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
|
||||
AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
|
||||
AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
|
||||
AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
|
||||
AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
|
||||
AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
|
||||
AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
|
||||
AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
|
||||
AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
|
||||
AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
|
||||
AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
|
||||
AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
|
||||
AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
|
||||
AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
|
||||
AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
|
||||
AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
|
||||
AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
|
||||
AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
|
||||
AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
|
||||
AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
|
||||
};
|
||||
|
||||
static const struct regulator_desc axp22x_regulators[] = {
|
||||
AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
|
||||
AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
|
||||
AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
|
||||
AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
|
||||
AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
|
||||
AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
|
||||
AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
|
||||
AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
|
||||
AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
|
||||
AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
|
||||
/* secondary switchable output of DCDC1 */
|
||||
AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100,
|
||||
AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
|
||||
/* LDO regulator internally chained to DCDC5 */
|
||||
AXP_DESC(AXP22X, DC5LDO, "dc5ldo", "dcdc5", 700, 1400, 100,
|
||||
AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
|
||||
AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
|
||||
AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
|
||||
AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
|
||||
AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
|
||||
AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
|
||||
AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
|
||||
AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
|
||||
AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
|
||||
AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
|
||||
AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
|
||||
AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
|
||||
AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
|
||||
AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
|
||||
AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
|
||||
AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
|
||||
AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
|
||||
AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
|
||||
AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
|
||||
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
|
||||
AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
|
||||
AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
|
||||
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
|
||||
AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
|
||||
};
|
||||
|
||||
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
|
||||
{
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
u32 min, max, def, step;
|
||||
|
||||
if (dcdcfreq < 750) {
|
||||
dcdcfreq = 750;
|
||||
dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n");
|
||||
switch (axp20x->variant) {
|
||||
case AXP202_ID:
|
||||
case AXP209_ID:
|
||||
min = 750;
|
||||
max = 1875;
|
||||
def = 1500;
|
||||
step = 75;
|
||||
break;
|
||||
case AXP221_ID:
|
||||
min = 1800;
|
||||
max = 4050;
|
||||
def = 3000;
|
||||
step = 150;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"Setting DCDC frequency for unsupported AXP variant\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dcdcfreq > 1875) {
|
||||
dcdcfreq = 1875;
|
||||
dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n");
|
||||
if (dcdcfreq == 0)
|
||||
dcdcfreq = def;
|
||||
|
||||
if (dcdcfreq < min) {
|
||||
dcdcfreq = min;
|
||||
dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
|
||||
min);
|
||||
}
|
||||
|
||||
dcdcfreq = (dcdcfreq - 750) / 75;
|
||||
if (dcdcfreq > max) {
|
||||
dcdcfreq = max;
|
||||
dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
|
||||
max);
|
||||
}
|
||||
|
||||
dcdcfreq = (dcdcfreq - min) / step;
|
||||
|
||||
return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
|
||||
AXP20X_FREQ_DCDC_MASK, dcdcfreq);
|
||||
|
@ -176,7 +280,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
|
|||
{
|
||||
struct device_node *np, *regulators;
|
||||
int ret;
|
||||
u32 dcdcfreq;
|
||||
u32 dcdcfreq = 0;
|
||||
|
||||
np = of_node_get(pdev->dev.parent->of_node);
|
||||
if (!np)
|
||||
|
@ -186,7 +290,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
|
|||
if (!regulators) {
|
||||
dev_warn(&pdev->dev, "regulators node not found\n");
|
||||
} else {
|
||||
dcdcfreq = 1500;
|
||||
of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
|
||||
ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
|
||||
if (ret < 0) {
|
||||
|
@ -202,15 +305,35 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
|
|||
|
||||
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
|
||||
{
|
||||
unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
|
||||
struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
|
||||
unsigned int mask;
|
||||
|
||||
if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
|
||||
switch (axp20x->variant) {
|
||||
case AXP202_ID:
|
||||
case AXP209_ID:
|
||||
if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
|
||||
return -EINVAL;
|
||||
|
||||
mask = AXP20X_WORKMODE_DCDC2_MASK;
|
||||
if (id == AXP20X_DCDC3)
|
||||
mask = AXP20X_WORKMODE_DCDC3_MASK;
|
||||
|
||||
workmode <<= ffs(mask) - 1;
|
||||
break;
|
||||
|
||||
case AXP221_ID:
|
||||
if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
|
||||
return -EINVAL;
|
||||
|
||||
mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
|
||||
workmode <<= id - AXP22X_DCDC1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should not happen */
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
|
||||
if (id == AXP20X_DCDC3)
|
||||
mask = AXP20X_WORKMODE_DCDC3_MASK;
|
||||
|
||||
workmode <<= ffs(mask) - 1;
|
||||
}
|
||||
|
||||
return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
|
||||
}
|
||||
|
@ -219,22 +342,40 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct regulator_dev *rdev;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
const struct regulator_desc *regulators;
|
||||
struct regulator_config config = {
|
||||
.dev = pdev->dev.parent,
|
||||
.regmap = axp20x->regmap,
|
||||
.driver_data = axp20x,
|
||||
};
|
||||
int ret, i;
|
||||
int ret, i, nregulators;
|
||||
u32 workmode;
|
||||
|
||||
switch (axp20x->variant) {
|
||||
case AXP202_ID:
|
||||
case AXP209_ID:
|
||||
regulators = axp20x_regulators;
|
||||
nregulators = AXP20X_REG_ID_MAX;
|
||||
break;
|
||||
case AXP221_ID:
|
||||
regulators = axp22x_regulators;
|
||||
nregulators = AXP22X_REG_ID_MAX;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
|
||||
axp20x->variant);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* This only sets the dcdc freq. Ignore any errors */
|
||||
axp20x_regulator_parse_dt(pdev);
|
||||
|
||||
for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
|
||||
rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i],
|
||||
for (i = 0; i < nregulators; i++) {
|
||||
rdev = devm_regulator_register(&pdev->dev, ®ulators[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&pdev->dev, "Failed to register %s\n",
|
||||
axp20x_regulators[i].name);
|
||||
regulators[i].name);
|
||||
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
@ -245,7 +386,7 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
|
|||
if (!ret) {
|
||||
if (axp20x_set_dcdc_workmode(rdev, i, workmode))
|
||||
dev_err(&pdev->dev, "Failed to set workmode on %s\n",
|
||||
axp20x_regulators[i].name);
|
||||
rdev->desc->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1520,6 +1520,17 @@ config RTC_DRV_SIRFSOC
|
|||
Say "yes" here to support the real time clock on SiRF SOC chips.
|
||||
This driver can also be built as a module called rtc-sirfsoc.
|
||||
|
||||
config RTC_DRV_ST_LPC
|
||||
tristate "STMicroelectronics LPC RTC"
|
||||
depends on ARCH_STI
|
||||
depends on OF
|
||||
help
|
||||
Say Y here to include STMicroelectronics Low Power Controller
|
||||
(LPC) based RTC support.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rtc-st-lpc.
|
||||
|
||||
config RTC_DRV_MOXART
|
||||
tristate "MOXA ART RTC"
|
||||
depends on ARCH_MOXART || COMPILE_TEST
|
||||
|
|
|
@ -154,4 +154,5 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
|
|||
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
|
||||
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
|
||||
obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
|
||||
obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
|
||||
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
|
||||
*
|
||||
* Copyright (C) 2014 STMicroelectronics Limited
|
||||
*
|
||||
* Author: David Paris <david.paris@st.com> for STMicroelectronics
|
||||
* Lee Jones <lee.jones@linaro.org> for STMicroelectronics
|
||||
*
|
||||
* Based on the original driver written by Stuart Menefy.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#include <dt-bindings/mfd/st-lpc.h>
|
||||
|
||||
/* Low Power Timer */
|
||||
#define LPC_LPT_LSB_OFF 0x400
|
||||
#define LPC_LPT_MSB_OFF 0x404
|
||||
#define LPC_LPT_START_OFF 0x408
|
||||
|
||||
/* Low Power Alarm */
|
||||
#define LPC_LPA_LSB_OFF 0x410
|
||||
#define LPC_LPA_MSB_OFF 0x414
|
||||
#define LPC_LPA_START_OFF 0x418
|
||||
|
||||
/* LPC as WDT */
|
||||
#define LPC_WDT_OFF 0x510
|
||||
#define LPC_WDT_FLAG_OFF 0x514
|
||||
|
||||
struct st_rtc {
|
||||
struct rtc_device *rtc_dev;
|
||||
struct rtc_wkalrm alarm;
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
unsigned long clkrate;
|
||||
void __iomem *ioaddr;
|
||||
bool irq_enabled:1;
|
||||
spinlock_t lock;
|
||||
short irq;
|
||||
};
|
||||
|
||||
static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
|
||||
unsigned long msb, unsigned long lsb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
|
||||
|
||||
writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
|
||||
writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
|
||||
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
|
||||
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t st_rtc_handler(int this_irq, void *data)
|
||||
{
|
||||
struct st_rtc *rtc = (struct st_rtc *)data;
|
||||
|
||||
rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
unsigned long lpt_lsb, lpt_msb;
|
||||
unsigned long long lpt;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
|
||||
do {
|
||||
lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF);
|
||||
lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF);
|
||||
} while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
|
||||
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
|
||||
lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
|
||||
do_div(lpt, rtc->clkrate);
|
||||
rtc_time_to_tm(lpt, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
unsigned long long lpt;
|
||||
unsigned long secs, flags;
|
||||
int ret;
|
||||
|
||||
ret = rtc_tm_to_time(tm, &secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lpt = (unsigned long long)secs * rtc->clkrate;
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
|
||||
writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
|
||||
writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF);
|
||||
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
|
||||
memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
|
||||
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled && !rtc->irq_enabled) {
|
||||
enable_irq(rtc->irq);
|
||||
rtc->irq_enabled = true;
|
||||
} else if (!enabled && rtc->irq_enabled) {
|
||||
disable_irq(rtc->irq);
|
||||
rtc->irq_enabled = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
struct rtc_time now;
|
||||
unsigned long now_secs;
|
||||
unsigned long alarm_secs;
|
||||
unsigned long long lpa;
|
||||
|
||||
st_rtc_read_time(dev, &now);
|
||||
rtc_tm_to_time(&now, &now_secs);
|
||||
rtc_tm_to_time(&t->time, &alarm_secs);
|
||||
|
||||
/* Invalid alarm time */
|
||||
if (now_secs > alarm_secs)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
|
||||
|
||||
/* Now many secs to fire */
|
||||
alarm_secs -= now_secs;
|
||||
lpa = (unsigned long long)alarm_secs * rtc->clkrate;
|
||||
|
||||
st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
|
||||
st_rtc_alarm_irq_enable(dev, t->enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops st_rtc_ops = {
|
||||
.read_time = st_rtc_read_time,
|
||||
.set_time = st_rtc_set_time,
|
||||
.read_alarm = st_rtc_read_alarm,
|
||||
.set_alarm = st_rtc_set_alarm,
|
||||
.alarm_irq_enable = st_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int st_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct st_rtc *rtc;
|
||||
struct resource *res;
|
||||
struct rtc_time tm_check;
|
||||
uint32_t mode;
|
||||
int ret = 0;
|
||||
|
||||
ret = of_property_read_u32(np, "st,lpc-mode", &mode);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "An LPC mode must be provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* LPC can either run in RTC or WDT mode */
|
||||
if (mode != ST_LPC_MODE_RTC)
|
||||
return -ENODEV;
|
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&rtc->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(rtc->ioaddr))
|
||||
return PTR_ERR(rtc->ioaddr);
|
||||
|
||||
rtc->irq = irq_of_parse_and_map(np, 0);
|
||||
if (!rtc->irq) {
|
||||
dev_err(&pdev->dev, "IRQ missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
|
||||
pdev->name, rtc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq_wake(rtc->irq);
|
||||
disable_irq(rtc->irq);
|
||||
|
||||
rtc->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(rtc->clk)) {
|
||||
dev_err(&pdev->dev, "Unable to request clock\n");
|
||||
return PTR_ERR(rtc->clk);
|
||||
}
|
||||
|
||||
clk_prepare_enable(rtc->clk);
|
||||
|
||||
rtc->clkrate = clk_get_rate(rtc->clk);
|
||||
if (!rtc->clkrate) {
|
||||
dev_err(&pdev->dev, "Unable to fetch clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, 1);
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
/*
|
||||
* The RTC-LPC is able to manage date.year > 2038
|
||||
* but currently the kernel can not manage this date!
|
||||
* If the RTC-LPC has a date.year > 2038 then
|
||||
* it's set to the epoch "Jan 1st 2000"
|
||||
*/
|
||||
st_rtc_read_time(&pdev->dev, &tm_check);
|
||||
|
||||
if (tm_check.tm_year >= (2038 - 1900)) {
|
||||
memset(&tm_check, 0, sizeof(tm_check));
|
||||
tm_check.tm_year = 100;
|
||||
tm_check.tm_mday = 1;
|
||||
st_rtc_set_time(&pdev->dev, &tm_check);
|
||||
}
|
||||
|
||||
rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
|
||||
&st_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc->rtc_dev)) {
|
||||
clk_disable_unprepare(rtc->clk);
|
||||
return PTR_ERR(rtc->rtc_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct st_rtc *rtc = platform_get_drvdata(pdev);
|
||||
|
||||
if (likely(rtc->rtc_dev))
|
||||
rtc_device_unregister(rtc->rtc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
return 0;
|
||||
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF);
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
rtc_alarm_irq_enable(rtc->rtc_dev, 0);
|
||||
|
||||
/*
|
||||
* clean 'rtc->alarm' to allow a new
|
||||
* .set_alarm to the upper RTC layer
|
||||
*/
|
||||
memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
|
||||
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
|
||||
writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
|
||||
writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
|
||||
|
||||
static const struct of_device_id st_rtc_match[] = {
|
||||
{ .compatible = "st,stih407-lpc" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_rtc_match);
|
||||
|
||||
static struct platform_driver st_rtc_platform_driver = {
|
||||
.driver = {
|
||||
.name = "st-lpc-rtc",
|
||||
.pm = &st_rtc_pm_ops,
|
||||
.of_match_table = st_rtc_match,
|
||||
},
|
||||
.probe = st_rtc_probe,
|
||||
.remove = st_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(st_rtc_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
|
||||
MODULE_AUTHOR("David Paris <david.paris@st.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -470,6 +470,18 @@ config SIRFSOC_WATCHDOG
|
|||
Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
|
||||
the watchdog triggers the system will be reset.
|
||||
|
||||
config ST_LPC_WATCHDOG
|
||||
tristate "STMicroelectronics LPC Watchdog"
|
||||
depends on ARCH_STI
|
||||
depends on OF
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here to include STMicroelectronics Low Power Controller
|
||||
(LPC) based Watchdog timer support.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called st_lpc_wdt.
|
||||
|
||||
config TEGRA_WATCHDOG
|
||||
tristate "Tegra watchdog"
|
||||
depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM
|
||||
|
|
|
@ -59,6 +59,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
|||
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
|
||||
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
|
||||
obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
|
||||
obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
|
||||
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
|
||||
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
|
||||
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* ST's LPC Watchdog
|
||||
*
|
||||
* Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
|
||||
*
|
||||
* Author: David Paris <david.paris@st.com> for STMicroelectronics
|
||||
* Lee Jones <lee.jones@linaro.org> for STMicroelectronics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include <dt-bindings/mfd/st-lpc.h>
|
||||
|
||||
/* Low Power Alarm */
|
||||
#define LPC_LPA_LSB_OFF 0x410
|
||||
#define LPC_LPA_START_OFF 0x418
|
||||
|
||||
/* LPC as WDT */
|
||||
#define LPC_WDT_OFF 0x510
|
||||
|
||||
static struct watchdog_device st_wdog_dev;
|
||||
|
||||
struct st_wdog_syscfg {
|
||||
unsigned int reset_type_reg;
|
||||
unsigned int reset_type_mask;
|
||||
unsigned int enable_reg;
|
||||
unsigned int enable_mask;
|
||||
};
|
||||
|
||||
struct st_wdog {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct st_wdog_syscfg *syscfg;
|
||||
struct clk *clk;
|
||||
unsigned long clkrate;
|
||||
bool warm_reset;
|
||||
};
|
||||
|
||||
static struct st_wdog_syscfg stid127_syscfg = {
|
||||
.reset_type_reg = 0x004,
|
||||
.reset_type_mask = BIT(2),
|
||||
.enable_reg = 0x000,
|
||||
.enable_mask = BIT(2),
|
||||
};
|
||||
|
||||
static struct st_wdog_syscfg stih415_syscfg = {
|
||||
.reset_type_reg = 0x0B8,
|
||||
.reset_type_mask = BIT(6),
|
||||
.enable_reg = 0x0B4,
|
||||
.enable_mask = BIT(7),
|
||||
};
|
||||
|
||||
static struct st_wdog_syscfg stih416_syscfg = {
|
||||
.reset_type_reg = 0x88C,
|
||||
.reset_type_mask = BIT(6),
|
||||
.enable_reg = 0x888,
|
||||
.enable_mask = BIT(7),
|
||||
};
|
||||
|
||||
static struct st_wdog_syscfg stih407_syscfg = {
|
||||
.enable_reg = 0x204,
|
||||
.enable_mask = BIT(19),
|
||||
};
|
||||
|
||||
static const struct of_device_id st_wdog_match[] = {
|
||||
{
|
||||
.compatible = "st,stih407-lpc",
|
||||
.data = &stih407_syscfg,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-lpc",
|
||||
.data = &stih416_syscfg,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih415-lpc",
|
||||
.data = &stih415_syscfg,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stid127-lpc",
|
||||
.data = &stid127_syscfg,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_wdog_match);
|
||||
|
||||
static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
|
||||
{
|
||||
/* Type of watchdog reset - 0: Cold 1: Warm */
|
||||
if (st_wdog->syscfg->reset_type_reg)
|
||||
regmap_update_bits(st_wdog->regmap,
|
||||
st_wdog->syscfg->reset_type_reg,
|
||||
st_wdog->syscfg->reset_type_mask,
|
||||
st_wdog->warm_reset);
|
||||
|
||||
/* Mask/unmask watchdog reset */
|
||||
regmap_update_bits(st_wdog->regmap,
|
||||
st_wdog->syscfg->enable_reg,
|
||||
st_wdog->syscfg->enable_mask,
|
||||
enable ? 0 : st_wdog->syscfg->enable_mask);
|
||||
}
|
||||
|
||||
static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
|
||||
{
|
||||
unsigned long clkrate = st_wdog->clkrate;
|
||||
|
||||
writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
|
||||
writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
|
||||
}
|
||||
|
||||
static int st_wdog_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
|
||||
|
||||
writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_wdog_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
|
||||
|
||||
writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_wdog_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
|
||||
|
||||
wdd->timeout = timeout;
|
||||
st_wdog_load_timer(st_wdog, timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_wdog_keepalive(struct watchdog_device *wdd)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
|
||||
|
||||
st_wdog_load_timer(st_wdog, wdd->timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info st_wdog_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "ST LPC WDT",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops st_wdog_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = st_wdog_start,
|
||||
.stop = st_wdog_stop,
|
||||
.ping = st_wdog_keepalive,
|
||||
.set_timeout = st_wdog_set_timeout,
|
||||
};
|
||||
|
||||
static struct watchdog_device st_wdog_dev = {
|
||||
.info = &st_wdog_info,
|
||||
.ops = &st_wdog_ops,
|
||||
};
|
||||
|
||||
static int st_wdog_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct st_wdog *st_wdog;
|
||||
struct regmap *regmap;
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
uint32_t mode;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "st,lpc-mode", &mode);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "An LPC mode must be provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* LPC can either run in RTC or WDT mode */
|
||||
if (mode != ST_LPC_MODE_WDT)
|
||||
return -ENODEV;
|
||||
|
||||
st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
|
||||
if (!st_wdog)
|
||||
return -ENOMEM;
|
||||
|
||||
match = of_match_device(st_wdog_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Couldn't match device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
st_wdog->syscfg = (struct st_wdog_syscfg *)match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&pdev->dev, "No syscfg phandle specified\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Unable to request clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
st_wdog->dev = &pdev->dev;
|
||||
st_wdog->base = base;
|
||||
st_wdog->clk = clk;
|
||||
st_wdog->regmap = regmap;
|
||||
st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset");
|
||||
st_wdog->clkrate = clk_get_rate(st_wdog->clk);
|
||||
|
||||
if (!st_wdog->clkrate) {
|
||||
dev_err(&pdev->dev, "Unable to fetch clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
watchdog_set_drvdata(&st_wdog_dev, st_wdog);
|
||||
watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
|
||||
|
||||
/* Init Watchdog timeout with value in DT */
|
||||
ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = watchdog_register_device(&st_wdog_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to register watchdog\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
st_wdog_setup(st_wdog, true);
|
||||
|
||||
dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
|
||||
st_wdog->warm_reset ? "warm" : "cold");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_wdog_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
|
||||
|
||||
st_wdog_setup(st_wdog, false);
|
||||
watchdog_unregister_device(&st_wdog_dev);
|
||||
clk_disable_unprepare(st_wdog->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st_wdog_suspend(struct device *dev)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
|
||||
|
||||
if (watchdog_active(&st_wdog_dev))
|
||||
st_wdog_stop(&st_wdog_dev);
|
||||
|
||||
st_wdog_setup(st_wdog, false);
|
||||
|
||||
clk_disable(st_wdog->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_wdog_resume(struct device *dev)
|
||||
{
|
||||
struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(st_wdog->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to re-enable clock\n");
|
||||
watchdog_unregister_device(&st_wdog_dev);
|
||||
clk_unprepare(st_wdog->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
st_wdog_setup(st_wdog, true);
|
||||
|
||||
if (watchdog_active(&st_wdog_dev)) {
|
||||
st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
|
||||
st_wdog_start(&st_wdog_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
|
||||
st_wdog_suspend,
|
||||
st_wdog_resume);
|
||||
|
||||
static struct platform_driver st_wdog_driver = {
|
||||
.driver = {
|
||||
.name = "st-lpc-wdt",
|
||||
.pm = &st_wdog_pm_ops,
|
||||
.of_match_table = st_wdog_match,
|
||||
},
|
||||
.probe = st_wdog_probe,
|
||||
.remove = st_wdog_remove,
|
||||
};
|
||||
module_platform_driver(st_wdog_driver);
|
||||
|
||||
MODULE_AUTHOR("David Paris <david.paris@st.com>");
|
||||
MODULE_DESCRIPTION("ST LPC Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -90,4 +90,18 @@
|
|||
#define ARIZONA_INMODE_SE 1
|
||||
#define ARIZONA_INMODE_DMIC 2
|
||||
|
||||
#define ARIZONA_MICD_TIME_CONTINUOUS 0
|
||||
#define ARIZONA_MICD_TIME_250US 1
|
||||
#define ARIZONA_MICD_TIME_500US 2
|
||||
#define ARIZONA_MICD_TIME_1MS 3
|
||||
#define ARIZONA_MICD_TIME_2MS 4
|
||||
#define ARIZONA_MICD_TIME_4MS 5
|
||||
#define ARIZONA_MICD_TIME_8MS 6
|
||||
#define ARIZONA_MICD_TIME_16MS 7
|
||||
#define ARIZONA_MICD_TIME_32MS 8
|
||||
#define ARIZONA_MICD_TIME_64MS 9
|
||||
#define ARIZONA_MICD_TIME_128MS 10
|
||||
#define ARIZONA_MICD_TIME_256MS 11
|
||||
#define ARIZONA_MICD_TIME_512MS 12
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* This header provides shared DT/Driver defines for ST's LPC device
|
||||
*
|
||||
* Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
|
||||
*
|
||||
* Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
|
||||
*/
|
||||
|
||||
#ifndef __DT_BINDINGS_ST_LPC_H__
|
||||
#define __DT_BINDINGS_ST_LPC_H__
|
||||
|
||||
#define ST_LPC_MODE_RTC 0
|
||||
#define ST_LPC_MODE_WDT 1
|
||||
|
||||
#endif /* __DT_BINDINGS_ST_LPC_H__ */
|
|
@ -675,6 +675,7 @@ struct twl4030_power_data {
|
|||
struct twl4030_resconfig *board_config;
|
||||
#define TWL4030_RESCONFIG_UNDEF ((u8)-1)
|
||||
bool use_poweroff; /* Board is wired for TWL poweroff */
|
||||
bool ac_charger_quirk; /* Disable AC charger on board */
|
||||
};
|
||||
|
||||
extern int twl4030_remove_script(u8 flags);
|
||||
|
|
|
@ -117,6 +117,7 @@ struct arizona {
|
|||
int num_core_supplies;
|
||||
struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES];
|
||||
struct regulator *dcvdd;
|
||||
bool has_fully_powered_off;
|
||||
|
||||
struct arizona_pdata pdata;
|
||||
|
||||
|
@ -153,7 +154,15 @@ int arizona_request_irq(struct arizona *arizona, int irq, char *name,
|
|||
void arizona_free_irq(struct arizona *arizona, int irq, void *data);
|
||||
int arizona_set_irq_wake(struct arizona *arizona, int irq, int on);
|
||||
|
||||
#ifdef CONFIG_MFD_WM5102
|
||||
int wm5102_patch(struct arizona *arizona);
|
||||
#else
|
||||
static inline int wm5102_patch(struct arizona *arizona)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int wm5110_patch(struct arizona *arizona);
|
||||
int wm8997_patch(struct arizona *arizona);
|
||||
|
||||
|
|
|
@ -156,7 +156,10 @@ struct arizona_pdata {
|
|||
/** MICBIAS configurations */
|
||||
struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS];
|
||||
|
||||
/** Mode of input structures */
|
||||
/**
|
||||
* Mode of input structures
|
||||
* One of the ARIZONA_INMODE_xxx values
|
||||
*/
|
||||
int inmode[ARIZONA_MAX_INPUT];
|
||||
|
||||
/** Mode for outputs */
|
||||
|
|
|
@ -2515,9 +2515,12 @@
|
|||
#define ARIZONA_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */
|
||||
#define ARIZONA_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */
|
||||
#define ARIZONA_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */
|
||||
#define ARIZONA_IN1_MODE_MASK 0x0400 /* IN1_MODE - [10] */
|
||||
#define ARIZONA_IN1_MODE_SHIFT 10 /* IN1_MODE - [10] */
|
||||
#define ARIZONA_IN1_MODE_WIDTH 1 /* IN1_MODE - [10] */
|
||||
#define ARIZONA_IN1_SINGLE_ENDED_MASK 0x0200 /* IN1_MODE - [9] */
|
||||
#define ARIZONA_IN1_SINGLE_ENDED_SHIFT 9 /* IN1_MODE - [9] */
|
||||
#define ARIZONA_IN1_SINGLE_ENDED_WIDTH 1 /* IN1_MODE - [9] */
|
||||
#define ARIZONA_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */
|
||||
|
@ -2588,9 +2591,12 @@
|
|||
#define ARIZONA_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */
|
||||
#define ARIZONA_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */
|
||||
#define ARIZONA_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */
|
||||
#define ARIZONA_IN2_MODE_MASK 0x0400 /* IN2_MODE - [10] */
|
||||
#define ARIZONA_IN2_MODE_SHIFT 10 /* IN2_MODE - [10] */
|
||||
#define ARIZONA_IN2_MODE_WIDTH 1 /* IN2_MODE - [10] */
|
||||
#define ARIZONA_IN2_SINGLE_ENDED_MASK 0x0200 /* IN2_MODE - [9] */
|
||||
#define ARIZONA_IN2_SINGLE_ENDED_SHIFT 9 /* IN2_MODE - [9] */
|
||||
#define ARIZONA_IN2_SINGLE_ENDED_WIDTH 1 /* IN2_MODE - [9] */
|
||||
#define ARIZONA_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */
|
||||
|
@ -2661,9 +2667,12 @@
|
|||
#define ARIZONA_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */
|
||||
#define ARIZONA_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */
|
||||
#define ARIZONA_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */
|
||||
#define ARIZONA_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */
|
||||
#define ARIZONA_IN3_MODE_MASK 0x0400 /* IN3_MODE - [10] */
|
||||
#define ARIZONA_IN3_MODE_SHIFT 10 /* IN3_MODE - [10] */
|
||||
#define ARIZONA_IN3_MODE_WIDTH 1 /* IN3_MODE - [10] */
|
||||
#define ARIZONA_IN3_SINGLE_ENDED_MASK 0x0200 /* IN3_MODE - [9] */
|
||||
#define ARIZONA_IN3_SINGLE_ENDED_SHIFT 9 /* IN3_MODE - [9] */
|
||||
#define ARIZONA_IN3_SINGLE_ENDED_WIDTH 1 /* IN3_MODE - [9] */
|
||||
#define ARIZONA_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */
|
||||
#define ARIZONA_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
enum {
|
||||
AXP202_ID = 0,
|
||||
AXP209_ID,
|
||||
AXP221_ID,
|
||||
AXP288_ID,
|
||||
NR_AXP20X_VARIANTS,
|
||||
};
|
||||
|
@ -45,6 +46,28 @@ enum {
|
|||
#define AXP20X_V_LTF_DISCHRG 0x3c
|
||||
#define AXP20X_V_HTF_DISCHRG 0x3d
|
||||
|
||||
#define AXP22X_PWR_OUT_CTRL1 0x10
|
||||
#define AXP22X_PWR_OUT_CTRL2 0x12
|
||||
#define AXP22X_PWR_OUT_CTRL3 0x13
|
||||
#define AXP22X_DLDO1_V_OUT 0x15
|
||||
#define AXP22X_DLDO2_V_OUT 0x16
|
||||
#define AXP22X_DLDO3_V_OUT 0x17
|
||||
#define AXP22X_DLDO4_V_OUT 0x18
|
||||
#define AXP22X_ELDO1_V_OUT 0x19
|
||||
#define AXP22X_ELDO2_V_OUT 0x1a
|
||||
#define AXP22X_ELDO3_V_OUT 0x1b
|
||||
#define AXP22X_DC5LDO_V_OUT 0x1c
|
||||
#define AXP22X_DCDC1_V_OUT 0x21
|
||||
#define AXP22X_DCDC2_V_OUT 0x22
|
||||
#define AXP22X_DCDC3_V_OUT 0x23
|
||||
#define AXP22X_DCDC4_V_OUT 0x24
|
||||
#define AXP22X_DCDC5_V_OUT 0x25
|
||||
#define AXP22X_DCDC23_V_RAMP_CTRL 0x27
|
||||
#define AXP22X_ALDO1_V_OUT 0x28
|
||||
#define AXP22X_ALDO2_V_OUT 0x29
|
||||
#define AXP22X_ALDO3_V_OUT 0x2a
|
||||
#define AXP22X_CHRG_CTRL3 0x35
|
||||
|
||||
/* Interrupt */
|
||||
#define AXP20X_IRQ1_EN 0x40
|
||||
#define AXP20X_IRQ2_EN 0x41
|
||||
|
@ -100,6 +123,9 @@ enum {
|
|||
#define AXP20X_VBUS_MON 0x8b
|
||||
#define AXP20X_OVER_TMP 0x8f
|
||||
|
||||
#define AXP22X_PWREN_CTRL1 0x8c
|
||||
#define AXP22X_PWREN_CTRL2 0x8d
|
||||
|
||||
/* GPIO */
|
||||
#define AXP20X_GPIO0_CTRL 0x90
|
||||
#define AXP20X_LDO5_V_OUT 0x91
|
||||
|
@ -108,6 +134,11 @@ enum {
|
|||
#define AXP20X_GPIO20_SS 0x94
|
||||
#define AXP20X_GPIO3_CTRL 0x95
|
||||
|
||||
#define AXP22X_LDO_IO0_V_OUT 0x91
|
||||
#define AXP22X_LDO_IO1_V_OUT 0x93
|
||||
#define AXP22X_GPIO_STATE 0x94
|
||||
#define AXP22X_GPIO_PULL_DOWN 0x95
|
||||
|
||||
/* Battery */
|
||||
#define AXP20X_CHRG_CC_31_24 0xb0
|
||||
#define AXP20X_CHRG_CC_23_16 0xb1
|
||||
|
@ -120,6 +151,9 @@ enum {
|
|||
#define AXP20X_CC_CTRL 0xb8
|
||||
#define AXP20X_FG_RES 0xb9
|
||||
|
||||
/* AXP22X specific registers */
|
||||
#define AXP22X_BATLOW_THRES1 0xe6
|
||||
|
||||
/* AXP288 specific registers */
|
||||
#define AXP288_PMIC_ADC_H 0x56
|
||||
#define AXP288_PMIC_ADC_L 0x57
|
||||
|
@ -158,6 +192,30 @@ enum {
|
|||
AXP20X_REG_ID_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
AXP22X_DCDC1 = 0,
|
||||
AXP22X_DCDC2,
|
||||
AXP22X_DCDC3,
|
||||
AXP22X_DCDC4,
|
||||
AXP22X_DCDC5,
|
||||
AXP22X_DC1SW,
|
||||
AXP22X_DC5LDO,
|
||||
AXP22X_ALDO1,
|
||||
AXP22X_ALDO2,
|
||||
AXP22X_ALDO3,
|
||||
AXP22X_ELDO1,
|
||||
AXP22X_ELDO2,
|
||||
AXP22X_ELDO3,
|
||||
AXP22X_DLDO1,
|
||||
AXP22X_DLDO2,
|
||||
AXP22X_DLDO3,
|
||||
AXP22X_DLDO4,
|
||||
AXP22X_RTC_LDO,
|
||||
AXP22X_LDO_IO0,
|
||||
AXP22X_LDO_IO1,
|
||||
AXP22X_REG_ID_MAX,
|
||||
};
|
||||
|
||||
/* IRQs */
|
||||
enum {
|
||||
AXP20X_IRQ_ACIN_OVER_V = 1,
|
||||
|
@ -199,6 +257,34 @@ enum {
|
|||
AXP20X_IRQ_GPIO0_INPUT,
|
||||
};
|
||||
|
||||
enum axp22x_irqs {
|
||||
AXP22X_IRQ_ACIN_OVER_V = 1,
|
||||
AXP22X_IRQ_ACIN_PLUGIN,
|
||||
AXP22X_IRQ_ACIN_REMOVAL,
|
||||
AXP22X_IRQ_VBUS_OVER_V,
|
||||
AXP22X_IRQ_VBUS_PLUGIN,
|
||||
AXP22X_IRQ_VBUS_REMOVAL,
|
||||
AXP22X_IRQ_VBUS_V_LOW,
|
||||
AXP22X_IRQ_BATT_PLUGIN,
|
||||
AXP22X_IRQ_BATT_REMOVAL,
|
||||
AXP22X_IRQ_BATT_ENT_ACT_MODE,
|
||||
AXP22X_IRQ_BATT_EXIT_ACT_MODE,
|
||||
AXP22X_IRQ_CHARG,
|
||||
AXP22X_IRQ_CHARG_DONE,
|
||||
AXP22X_IRQ_BATT_TEMP_HIGH,
|
||||
AXP22X_IRQ_BATT_TEMP_LOW,
|
||||
AXP22X_IRQ_DIE_TEMP_HIGH,
|
||||
AXP22X_IRQ_PEK_SHORT,
|
||||
AXP22X_IRQ_PEK_LONG,
|
||||
AXP22X_IRQ_LOW_PWR_LVL1,
|
||||
AXP22X_IRQ_LOW_PWR_LVL2,
|
||||
AXP22X_IRQ_TIMER,
|
||||
AXP22X_IRQ_PEK_RIS_EDGE,
|
||||
AXP22X_IRQ_PEK_FAL_EDGE,
|
||||
AXP22X_IRQ_GPIO1_INPUT,
|
||||
AXP22X_IRQ_GPIO0_INPUT,
|
||||
};
|
||||
|
||||
enum axp288_irqs {
|
||||
AXP288_IRQ_VBUS_FALL = 2,
|
||||
AXP288_IRQ_VBUS_RISE,
|
||||
|
|
|
@ -17,10 +17,29 @@
|
|||
#define __LINUX_MFD_CROS_EC_H
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define CROS_EC_DEV_NAME "cros_ec"
|
||||
#define CROS_EC_DEV_PD_NAME "cros_pd"
|
||||
|
||||
/*
|
||||
* The EC is unresponsive for a time after a reboot command. Add a
|
||||
* simple delay to make sure that the bus stays locked.
|
||||
*/
|
||||
#define EC_REBOOT_DELAY_MS 50
|
||||
|
||||
/*
|
||||
* Max bus-specific overhead incurred by request/responses.
|
||||
* I2C requires 1 additional byte for requests.
|
||||
* I2C requires 2 additional bytes for responses.
|
||||
* */
|
||||
#define EC_PROTO_VERSION_UNKNOWN 0
|
||||
#define EC_MAX_REQUEST_OVERHEAD 1
|
||||
#define EC_MAX_RESPONSE_OVERHEAD 2
|
||||
|
||||
/*
|
||||
* Command interface between EC and AP, for LPC, I2C and SPI interfaces.
|
||||
*/
|
||||
|
@ -42,8 +61,7 @@ enum {
|
|||
* @outsize: Outgoing length in bytes
|
||||
* @insize: Max number of bytes to accept from EC
|
||||
* @result: EC's response to the command (separate from communication failure)
|
||||
* @outdata: Outgoing data to EC
|
||||
* @indata: Where to put the incoming data from EC
|
||||
* @data: Where to put the incoming data from EC and outgoing data to EC
|
||||
*/
|
||||
struct cros_ec_command {
|
||||
uint32_t version;
|
||||
|
@ -51,18 +69,14 @@ struct cros_ec_command {
|
|||
uint32_t outsize;
|
||||
uint32_t insize;
|
||||
uint32_t result;
|
||||
uint8_t outdata[EC_PROTO2_MAX_PARAM_SIZE];
|
||||
uint8_t indata[EC_PROTO2_MAX_PARAM_SIZE];
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cros_ec_device - Information about a ChromeOS EC device
|
||||
*
|
||||
* @ec_name: name of EC device (e.g. 'chromeos-ec')
|
||||
* @phys_name: name of physical comms layer (e.g. 'i2c-4')
|
||||
* @dev: Device pointer for physical comms device
|
||||
* @vdev: Device pointer for virtual comms device
|
||||
* @cdev: Character device structure for virtual comms device
|
||||
* @was_wake_device: true if this device was set to wake the system from
|
||||
* sleep at the last suspend
|
||||
* @cmd_readmem: direct read of the EC memory-mapped region, if supported
|
||||
|
@ -74,6 +88,7 @@ struct cros_ec_command {
|
|||
*
|
||||
* @priv: Private data
|
||||
* @irq: Interrupt to use
|
||||
* @id: Device id
|
||||
* @din: input buffer (for data from EC)
|
||||
* @dout: output buffer (for data to EC)
|
||||
* \note
|
||||
|
@ -85,41 +100,72 @@ struct cros_ec_command {
|
|||
* to using dword.
|
||||
* @din_size: size of din buffer to allocate (zero to use static din)
|
||||
* @dout_size: size of dout buffer to allocate (zero to use static dout)
|
||||
* @parent: pointer to parent device (e.g. i2c or spi device)
|
||||
* @wake_enabled: true if this device can wake the system from sleep
|
||||
* @cmd_xfer: send command to EC and get response
|
||||
* Returns the number of bytes received if the communication succeeded, but
|
||||
* that doesn't mean the EC was happy with the command. The caller
|
||||
* should check msg.result for the EC's result code.
|
||||
* @pkt_xfer: send packet to EC and get response
|
||||
* @lock: one transaction at a time
|
||||
*/
|
||||
struct cros_ec_device {
|
||||
|
||||
/* These are used by other drivers that want to talk to the EC */
|
||||
const char *ec_name;
|
||||
const char *phys_name;
|
||||
struct device *dev;
|
||||
struct device *vdev;
|
||||
struct cdev cdev;
|
||||
bool was_wake_device;
|
||||
struct class *cros_class;
|
||||
int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset,
|
||||
unsigned int bytes, void *dest);
|
||||
|
||||
/* These are used to implement the platform-specific interface */
|
||||
u16 max_request;
|
||||
u16 max_response;
|
||||
u16 max_passthru;
|
||||
u16 proto_version;
|
||||
void *priv;
|
||||
int irq;
|
||||
uint8_t *din;
|
||||
uint8_t *dout;
|
||||
u8 *din;
|
||||
u8 *dout;
|
||||
int din_size;
|
||||
int dout_size;
|
||||
struct device *parent;
|
||||
bool wake_enabled;
|
||||
int (*cmd_xfer)(struct cros_ec_device *ec,
|
||||
struct cros_ec_command *msg);
|
||||
int (*pkt_xfer)(struct cros_ec_device *ec,
|
||||
struct cros_ec_command *msg);
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/* struct cros_ec_platform - ChromeOS EC platform information
|
||||
*
|
||||
* @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
|
||||
* used in /dev/ and sysfs.
|
||||
* @cmd_offset: offset to apply for each command. Set when
|
||||
* registering a devicde behind another one.
|
||||
*/
|
||||
struct cros_ec_platform {
|
||||
const char *ec_name;
|
||||
u16 cmd_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct cros_ec_dev - ChromeOS EC device entry point
|
||||
*
|
||||
* @class_dev: Device structure used in sysfs
|
||||
* @cdev: Character device structure in /dev
|
||||
* @ec_dev: cros_ec_device structure to talk to the physical device
|
||||
* @dev: pointer to the platform device
|
||||
* @cmd_offset: offset to apply for each command.
|
||||
*/
|
||||
struct cros_ec_dev {
|
||||
struct device class_dev;
|
||||
struct cdev cdev;
|
||||
struct cros_ec_device *ec_dev;
|
||||
struct device *dev;
|
||||
u16 cmd_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device
|
||||
*
|
||||
|
@ -198,4 +244,16 @@ int cros_ec_remove(struct cros_ec_device *ec_dev);
|
|||
*/
|
||||
int cros_ec_register(struct cros_ec_device *ec_dev);
|
||||
|
||||
/**
|
||||
* cros_ec_register - Query the protocol version supported by the ChromeOS EC
|
||||
*
|
||||
* @ec_dev: Device to register
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
int cros_ec_query_all(struct cros_ec_device *ec_dev);
|
||||
|
||||
/* sysfs stuff */
|
||||
extern struct attribute_group cros_ec_attr_group;
|
||||
extern struct attribute_group cros_ec_lightbar_attr_group;
|
||||
|
||||
#endif /* __LINUX_MFD_CROS_EC_H */
|
||||
|
|
|
@ -515,7 +515,7 @@ struct ec_host_response {
|
|||
/*
|
||||
* Notes on commands:
|
||||
*
|
||||
* Each command is an 8-byte command value. Commands which take params or
|
||||
* Each command is an 16-bit command value. Commands which take params or
|
||||
* return response data specify structs for that data. If no struct is
|
||||
* specified, the command does not input or output data, respectively.
|
||||
* Parameter/response length is implicit in the structs. Some underlying
|
||||
|
@ -966,7 +966,7 @@ struct rgb_s {
|
|||
/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a
|
||||
* host command, but the alignment is the same regardless. Keep it that way.
|
||||
*/
|
||||
struct lightbar_params {
|
||||
struct lightbar_params_v0 {
|
||||
/* Timing */
|
||||
int32_t google_ramp_up;
|
||||
int32_t google_ramp_down;
|
||||
|
@ -1000,32 +1000,81 @@ struct lightbar_params {
|
|||
struct rgb_s color[8]; /* 0-3 are Google colors */
|
||||
} __packed;
|
||||
|
||||
struct lightbar_params_v1 {
|
||||
/* Timing */
|
||||
int32_t google_ramp_up;
|
||||
int32_t google_ramp_down;
|
||||
int32_t s3s0_ramp_up;
|
||||
int32_t s0_tick_delay[2]; /* AC=0/1 */
|
||||
int32_t s0a_tick_delay[2]; /* AC=0/1 */
|
||||
int32_t s0s3_ramp_down;
|
||||
int32_t s3_sleep_for;
|
||||
int32_t s3_ramp_up;
|
||||
int32_t s3_ramp_down;
|
||||
int32_t tap_tick_delay;
|
||||
int32_t tap_display_time;
|
||||
|
||||
/* Tap-for-battery params */
|
||||
uint8_t tap_pct_red;
|
||||
uint8_t tap_pct_green;
|
||||
uint8_t tap_seg_min_on;
|
||||
uint8_t tap_seg_max_on;
|
||||
uint8_t tap_seg_osc;
|
||||
uint8_t tap_idx[3];
|
||||
|
||||
/* Oscillation */
|
||||
uint8_t osc_min[2]; /* AC=0/1 */
|
||||
uint8_t osc_max[2]; /* AC=0/1 */
|
||||
uint8_t w_ofs[2]; /* AC=0/1 */
|
||||
|
||||
/* Brightness limits based on the backlight and AC. */
|
||||
uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */
|
||||
uint8_t bright_bl_on_min[2]; /* AC=0/1 */
|
||||
uint8_t bright_bl_on_max[2]; /* AC=0/1 */
|
||||
|
||||
/* Battery level thresholds */
|
||||
uint8_t battery_threshold[LB_BATTERY_LEVELS - 1];
|
||||
|
||||
/* Map [AC][battery_level] to color index */
|
||||
uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */
|
||||
uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */
|
||||
|
||||
/* Color palette */
|
||||
struct rgb_s color[8]; /* 0-3 are Google colors */
|
||||
} __packed;
|
||||
|
||||
struct ec_params_lightbar {
|
||||
uint8_t cmd; /* Command (see enum lightbar_command) */
|
||||
union {
|
||||
struct {
|
||||
/* no args */
|
||||
} dump, off, on, init, get_seq, get_params, version;
|
||||
} dump, off, on, init, get_seq, get_params_v0, get_params_v1,
|
||||
version, get_brightness, get_demo;
|
||||
|
||||
struct num {
|
||||
struct {
|
||||
uint8_t num;
|
||||
} brightness, seq, demo;
|
||||
} set_brightness, seq, demo;
|
||||
|
||||
struct reg {
|
||||
struct {
|
||||
uint8_t ctrl, reg, value;
|
||||
} reg;
|
||||
|
||||
struct rgb {
|
||||
struct {
|
||||
uint8_t led, red, green, blue;
|
||||
} rgb;
|
||||
} set_rgb;
|
||||
|
||||
struct lightbar_params set_params;
|
||||
struct {
|
||||
uint8_t led;
|
||||
} get_rgb;
|
||||
|
||||
struct lightbar_params_v0 set_params_v0;
|
||||
struct lightbar_params_v1 set_params_v1;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct ec_response_lightbar {
|
||||
union {
|
||||
struct dump {
|
||||
struct {
|
||||
struct {
|
||||
uint8_t reg;
|
||||
uint8_t ic0;
|
||||
|
@ -1033,20 +1082,26 @@ struct ec_response_lightbar {
|
|||
} vals[23];
|
||||
} dump;
|
||||
|
||||
struct get_seq {
|
||||
struct {
|
||||
uint8_t num;
|
||||
} get_seq;
|
||||
} get_seq, get_brightness, get_demo;
|
||||
|
||||
struct lightbar_params get_params;
|
||||
struct lightbar_params_v0 get_params_v0;
|
||||
struct lightbar_params_v1 get_params_v1;
|
||||
|
||||
struct version {
|
||||
struct {
|
||||
uint32_t num;
|
||||
uint32_t flags;
|
||||
} version;
|
||||
|
||||
struct {
|
||||
uint8_t red, green, blue;
|
||||
} get_rgb;
|
||||
|
||||
struct {
|
||||
/* no return params */
|
||||
} off, on, init, brightness, seq, reg, rgb, demo, set_params;
|
||||
} off, on, init, set_brightness, seq, reg, set_rgb,
|
||||
demo, set_params_v0, set_params_v1;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
@ -1056,15 +1111,20 @@ enum lightbar_command {
|
|||
LIGHTBAR_CMD_OFF = 1,
|
||||
LIGHTBAR_CMD_ON = 2,
|
||||
LIGHTBAR_CMD_INIT = 3,
|
||||
LIGHTBAR_CMD_BRIGHTNESS = 4,
|
||||
LIGHTBAR_CMD_SET_BRIGHTNESS = 4,
|
||||
LIGHTBAR_CMD_SEQ = 5,
|
||||
LIGHTBAR_CMD_REG = 6,
|
||||
LIGHTBAR_CMD_RGB = 7,
|
||||
LIGHTBAR_CMD_SET_RGB = 7,
|
||||
LIGHTBAR_CMD_GET_SEQ = 8,
|
||||
LIGHTBAR_CMD_DEMO = 9,
|
||||
LIGHTBAR_CMD_GET_PARAMS = 10,
|
||||
LIGHTBAR_CMD_SET_PARAMS = 11,
|
||||
LIGHTBAR_CMD_GET_PARAMS_V0 = 10,
|
||||
LIGHTBAR_CMD_SET_PARAMS_V0 = 11,
|
||||
LIGHTBAR_CMD_VERSION = 12,
|
||||
LIGHTBAR_CMD_GET_BRIGHTNESS = 13,
|
||||
LIGHTBAR_CMD_GET_RGB = 14,
|
||||
LIGHTBAR_CMD_GET_DEMO = 15,
|
||||
LIGHTBAR_CMD_GET_PARAMS_V1 = 16,
|
||||
LIGHTBAR_CMD_SET_PARAMS_V1 = 17,
|
||||
LIGHTBAR_NUM_CMDS
|
||||
};
|
||||
|
||||
|
@ -1421,8 +1481,40 @@ struct ec_response_rtc {
|
|||
/*****************************************************************************/
|
||||
/* Port80 log access */
|
||||
|
||||
/* Maximum entries that can be read/written in a single command */
|
||||
#define EC_PORT80_SIZE_MAX 32
|
||||
|
||||
/* Get last port80 code from previous boot */
|
||||
#define EC_CMD_PORT80_LAST_BOOT 0x48
|
||||
#define EC_CMD_PORT80_READ 0x48
|
||||
|
||||
enum ec_port80_subcmd {
|
||||
EC_PORT80_GET_INFO = 0,
|
||||
EC_PORT80_READ_BUFFER,
|
||||
};
|
||||
|
||||
struct ec_params_port80_read {
|
||||
uint16_t subcmd;
|
||||
union {
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint32_t num_entries;
|
||||
} read_buffer;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct ec_response_port80_read {
|
||||
union {
|
||||
struct {
|
||||
uint32_t writes;
|
||||
uint32_t history_size;
|
||||
uint32_t last_boot;
|
||||
} get_info;
|
||||
struct {
|
||||
uint16_t codes[EC_PORT80_SIZE_MAX];
|
||||
} data;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct ec_response_port80_last_boot {
|
||||
uint16_t code;
|
||||
|
@ -1782,6 +1874,7 @@ struct ec_params_gpio_set {
|
|||
/* Get GPIO value */
|
||||
#define EC_CMD_GPIO_GET 0x93
|
||||
|
||||
/* Version 0 of input params and response */
|
||||
struct ec_params_gpio_get {
|
||||
char name[32];
|
||||
} __packed;
|
||||
|
@ -1789,6 +1882,38 @@ struct ec_response_gpio_get {
|
|||
uint8_t val;
|
||||
} __packed;
|
||||
|
||||
/* Version 1 of input params and response */
|
||||
struct ec_params_gpio_get_v1 {
|
||||
uint8_t subcmd;
|
||||
union {
|
||||
struct {
|
||||
char name[32];
|
||||
} get_value_by_name;
|
||||
struct {
|
||||
uint8_t index;
|
||||
} get_info;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct ec_response_gpio_get_v1 {
|
||||
union {
|
||||
struct {
|
||||
uint8_t val;
|
||||
} get_value_by_name, get_count;
|
||||
struct {
|
||||
uint8_t val;
|
||||
char name[32];
|
||||
uint32_t flags;
|
||||
} get_info;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
enum gpio_get_subcmd {
|
||||
EC_GPIO_GET_BY_NAME = 0,
|
||||
EC_GPIO_GET_COUNT = 1,
|
||||
EC_GPIO_GET_INFO = 2,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* I2C commands. Only available when flash write protect is unlocked. */
|
||||
|
||||
|
@ -1857,13 +1982,21 @@ struct ec_params_charge_control {
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Cut off battery power output if the battery supports.
|
||||
* Cut off battery power immediately or after the host has shut down.
|
||||
*
|
||||
* For unsupported battery, just don't implement this command and lets EC
|
||||
* return EC_RES_INVALID_COMMAND.
|
||||
* return EC_RES_INVALID_COMMAND if unsupported by a board/battery.
|
||||
* EC_RES_SUCCESS if the command was successful.
|
||||
* EC_RES_ERROR if the cut off command failed.
|
||||
*/
|
||||
|
||||
#define EC_CMD_BATTERY_CUT_OFF 0x99
|
||||
|
||||
#define EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN (1 << 0)
|
||||
|
||||
struct ec_params_battery_cutoff {
|
||||
uint8_t flags;
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* USB port mux control. */
|
||||
|
||||
|
@ -2141,6 +2274,32 @@ struct ec_params_sb_wr_block {
|
|||
uint16_t data[32];
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Battery vendor parameters
|
||||
*
|
||||
* Get or set vendor-specific parameters in the battery. Implementations may
|
||||
* differ between boards or batteries. On a set operation, the response
|
||||
* contains the actual value set, which may be rounded or clipped from the
|
||||
* requested value.
|
||||
*/
|
||||
|
||||
#define EC_CMD_BATTERY_VENDOR_PARAM 0xb4
|
||||
|
||||
enum ec_battery_vendor_param_mode {
|
||||
BATTERY_VENDOR_PARAM_MODE_GET = 0,
|
||||
BATTERY_VENDOR_PARAM_MODE_SET,
|
||||
};
|
||||
|
||||
struct ec_params_battery_vendor_param {
|
||||
uint32_t param;
|
||||
uint32_t value;
|
||||
uint8_t mode;
|
||||
} __packed;
|
||||
|
||||
struct ec_response_battery_vendor_param {
|
||||
uint32_t value;
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* System commands */
|
||||
|
||||
|
@ -2336,6 +2495,80 @@ struct ec_params_reboot_ec {
|
|||
|
||||
#endif /* !__ACPI__ */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* PD commands
|
||||
*
|
||||
* These commands are for PD MCU communication.
|
||||
*/
|
||||
|
||||
/* EC to PD MCU exchange status command */
|
||||
#define EC_CMD_PD_EXCHANGE_STATUS 0x100
|
||||
|
||||
/* Status of EC being sent to PD */
|
||||
struct ec_params_pd_status {
|
||||
int8_t batt_soc; /* battery state of charge */
|
||||
} __packed;
|
||||
|
||||
/* Status of PD being sent back to EC */
|
||||
struct ec_response_pd_status {
|
||||
int8_t status; /* PD MCU status */
|
||||
uint32_t curr_lim_ma; /* input current limit */
|
||||
} __packed;
|
||||
|
||||
/* Set USB type-C port role and muxes */
|
||||
#define EC_CMD_USB_PD_CONTROL 0x101
|
||||
|
||||
enum usb_pd_control_role {
|
||||
USB_PD_CTRL_ROLE_NO_CHANGE = 0,
|
||||
USB_PD_CTRL_ROLE_TOGGLE_ON = 1, /* == AUTO */
|
||||
USB_PD_CTRL_ROLE_TOGGLE_OFF = 2,
|
||||
USB_PD_CTRL_ROLE_FORCE_SINK = 3,
|
||||
USB_PD_CTRL_ROLE_FORCE_SOURCE = 4,
|
||||
};
|
||||
|
||||
enum usb_pd_control_mux {
|
||||
USB_PD_CTRL_MUX_NO_CHANGE = 0,
|
||||
USB_PD_CTRL_MUX_NONE = 1,
|
||||
USB_PD_CTRL_MUX_USB = 2,
|
||||
USB_PD_CTRL_MUX_DP = 3,
|
||||
USB_PD_CTRL_MUX_DOCK = 4,
|
||||
USB_PD_CTRL_MUX_AUTO = 5,
|
||||
};
|
||||
|
||||
struct ec_params_usb_pd_control {
|
||||
uint8_t port;
|
||||
uint8_t role;
|
||||
uint8_t mux;
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Passthru commands
|
||||
*
|
||||
* Some platforms have sub-processors chained to each other. For example.
|
||||
*
|
||||
* AP <--> EC <--> PD MCU
|
||||
*
|
||||
* The top 2 bits of the command number are used to indicate which device the
|
||||
* command is intended for. Device 0 is always the device receiving the
|
||||
* command; other device mapping is board-specific.
|
||||
*
|
||||
* When a device receives a command to be passed to a sub-processor, it passes
|
||||
* it on with the device number set back to 0. This allows the sub-processor
|
||||
* to remain blissfully unaware of whether the command originated on the next
|
||||
* device up the chain, or was passed through from the AP.
|
||||
*
|
||||
* In the above example, if the AP wants to send command 0x0002 to the PD MCU,
|
||||
* AP sends command 0x4002 to the EC
|
||||
* EC sends command 0x0002 to the PD MCU
|
||||
* EC forwards PD MCU response back to the AP
|
||||
*/
|
||||
|
||||
/* Offset and max command number for sub-device n */
|
||||
#define EC_CMD_PASSTHRU_OFFSET(n) (0x4000 * (n))
|
||||
#define EC_CMD_PASSTHRU_MAX(n) (EC_CMD_PASSTHRU_OFFSET(n) + 0x3fff)
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Deprecated constants. These constants have been renamed for clarity. The
|
||||
|
|
|
@ -89,6 +89,6 @@ static inline int da9055_reg_update(struct da9055 *da9055, unsigned char reg,
|
|||
int da9055_device_init(struct da9055 *da9055);
|
||||
void da9055_device_exit(struct da9055 *da9055);
|
||||
|
||||
extern struct regmap_config da9055_regmap_config;
|
||||
extern const struct regmap_config da9055_regmap_config;
|
||||
|
||||
#endif /* __DA9055_CORE_H */
|
||||
|
|
|
@ -125,9 +125,4 @@ enum max77686_opmode {
|
|||
MAX77686_OPMODE_STANDBY,
|
||||
};
|
||||
|
||||
struct max77686_opmode_data {
|
||||
int id;
|
||||
int mode;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_MAX77686_H */
|
||||
|
|
Loading…
Reference in New Issue