Commit 84e9c87e authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 7fe0bf90 1abf25a2
......@@ -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
......
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";
......
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>;
};
};
};
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>;
};
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;
};
......@@ -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,
......
This diff is collapsed.
......@@ -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;
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);
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);
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;
static struct cros_ec_platform ec_p = {
.ec_name = CROS_EC_DEV_NAME,
.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
};
msg->result = status_msg.result;
if (status_msg.result != EC_RES_SUCCESS)
break;
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),
};
status = (struct ec_response_get_comms_status *)
status_msg.indata;
if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
break;
}
}
mutex_unlock(&ec_dev->lock);
static const struct mfd_cell ec_cell = {
.name = "cros-ec-ctl",
.platform_data = &ec_p,
.pdata_size = sizeof(ec_p),
};
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) {
......
This diff is collapsed.
......@@ -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 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
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 @@ static int twl4030_config_wakeup3_sequence(u8 address)
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);
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
if (!msg)
return -ENOMEM;
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)
return ret;
goto exit;
if (msg.result != EC_RES_SUCCESS) {
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;
if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
return -EFAULT;
goto exit;
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);
if (!ec)
return retval;
/* Instantiate it (and remember the EC) */
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 @@ static int lb_throttle(void)
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;
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_device *ec,
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);
msg = alloc_lightbar_cmd_msg(ec);
if (!msg)
return -ENOMEM;
param = (struct ec_params_lightbar *)msg.outdata;
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) {
......
/*
* 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);
resp = (struct ec_response_flash_info *)msg.indata;
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);
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->data;
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);
}
This diff is collapsed.
......@@ -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
/*
* 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
......
This diff is collapsed.
......@@ -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
/*
* 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] */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment