summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-23 08:18:01 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-23 08:18:01 -0800
commitdf9cdc1727ed9debfce59c5f600d794a63fcbfeb (patch)
tree03438886f80e90213a6be5b21ac0d601584213a5
parentbc49a7831b1137ce1c2dda1c57e3631655f5d2ae (diff)
parente93c10211d03c35271896b03a40d3eca4a674770 (diff)
Merge tag 'mfd-for-linus-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones: "Core Frameworks: - Add new !TOUCHSCREEN_SUN4I dependency for SUN4I_GPADC - List include/dt-bindings/mfd/* to files supported in MAINTAINERS New Drivers: - Intel Apollo Lake SPI NOR - ST STM32 Timers (Advanced, Basic and PWM) - Motorola 6556002 CPCAP (PMIC) New Device Support: - Add support for AXP221 to axp20x - Add support for Intel Gemini Lake to intel-lpss-pci - Add support for MT6323 LED to mt6397-core - Add support for COMe-bBD#, COMe-bSL6, COMe-bKL6, COMe-cAL6 and COMe-cKL6 to kempld-core New Functionality: - Add support for Analog CODAC to sun6i-prcm - Add support for Watchdog to lpc_ich Fix-ups: - Error handling improvements; axp288_charger, axp20x, ab8500-sysctrl - Adapt platform data handling; axp20x - IRQ handling improvements; arizona, axp20x - Remove superfluous code; arizona, axp20x, lpc_ich - Trivial coding style/spelling fixes; axp20x, abx500, mfd.txt - Regmap fix-ups; axp20x - DT changes; mfd.txt, aspeed-lpc, aspeed-gfx, ab8500-core, tps65912, mt6397 - Use new I2C probing mechanism; max77686 - Constification; rk808 Bug Fixes: - Stop data transfer whilst suspended; cros_ec" * tag 'mfd-for-linus-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (43 commits) mfd: lpc_ich: Enable watchdog on Intel Apollo Lake PCH mfd: lpc_ich: Remove useless comments in core part mfd: Add support for several boards to Kontron PLD driver mfd: constify regmap_irq_chip structures MAINTAINERS: Add include/dt-bindings/mfd to MFD entry mfd: cpcap: Add minimal support mfd: mt6397: Add MT6323 LED support into MT6397 driver Documentation: devicetree: Add LED subnode binding for MT6323 PMIC mfd: tps65912: Export OF device ID table as module aliases mfd: ab8500-core: Rename clock device and compatible mfd: cros_ec: Send correct suspend/resume event to EC mfd: max77686: Remove I2C device ID table mfd: max77686: Use the struct i2c_driver .probe_new instead of .probe mfd: max77686: Use of_device_get_match_data() helper mfd: max77686: Don't attempt to get i2c_device_id .data mfd: ab8500-sysctrl: Handle probe deferral mfd: intel-lpss: Add Intel Gemini Lake PCI IDs mfd: axp20x: Fix AXP806 access errors on cold boot mfd: cros_ec: Send suspend state notification to EC mfd: cros_ec: Prevent data transfer while device is suspended ...
-rw-r--r--Documentation/devicetree/bindings/mfd/aspeed-gfx.txt17
-rw-r--r--Documentation/devicetree/bindings/mfd/aspeed-lpc.txt137
-rw-r--r--Documentation/devicetree/bindings/mfd/mfd.txt12
-rw-r--r--Documentation/devicetree/bindings/mfd/motorola-cpcap.txt31
-rw-r--r--Documentation/devicetree/bindings/mfd/mt6397.txt4
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c452
-rw-r--r--drivers/mfd/Kconfig17
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/ab8500-core.c4
-rw-r--r--drivers/mfd/ab8500-sysctrl.c14
-rw-r--r--drivers/mfd/arizona-irq.c86
-rw-r--r--drivers/mfd/arizona.h2
-rw-r--r--drivers/mfd/axp20x.c78
-rw-r--r--drivers/mfd/cros_ec.c53
-rw-r--r--drivers/mfd/intel-lpss-pci.c17
-rw-r--r--drivers/mfd/kempld-core.c40
-rw-r--r--drivers/mfd/lpc_ich.c17
-rw-r--r--drivers/mfd/max77686.c25
-rw-r--r--drivers/mfd/motorola-cpcap.c259
-rw-r--r--drivers/mfd/mt6397-core.c4
-rw-r--r--drivers/mfd/rk808.c4
-rw-r--r--drivers/mfd/sun6i-prcm.c13
-rw-r--r--drivers/mfd/tps65912-i2c.c1
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c5
-rw-r--r--include/linux/mfd/abx500.h2
-rw-r--r--include/linux/mfd/abx500/ab8500-bm.h4
-rw-r--r--include/linux/mfd/axp20x.h20
-rw-r--r--include/linux/mfd/cros_ec.h2
-rw-r--r--include/linux/mfd/cros_ec_commands.h88
-rw-r--r--include/linux/mfd/motorola-cpcap.h292
31 files changed, 1558 insertions, 144 deletions
diff --git a/Documentation/devicetree/bindings/mfd/aspeed-gfx.txt b/Documentation/devicetree/bindings/mfd/aspeed-gfx.txt
new file mode 100644
index 000000000000..aea5370efd97
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/aspeed-gfx.txt
@@ -0,0 +1,17 @@
+* Device tree bindings for Aspeed SoC Display Controller (GFX)
+
+The Aspeed SoC Display Controller primarily does as its name suggests, but also
+participates in pinmux requests on the g5 SoCs. It is therefore considered a
+syscon device.
+
+Required properties:
+- compatible: "aspeed,ast2500-gfx", "syscon"
+- reg: contains offset/length value of the GFX memory
+ region.
+
+Example:
+
+gfx: display@1e6e6000 {
+ compatible = "aspeed,ast2500-gfx", "syscon";
+ reg = <0x1e6e6000 0x1000>;
+};
diff --git a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
new file mode 100644
index 000000000000..514d82ced95b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
@@ -0,0 +1,137 @@
+======================================================================
+Device tree bindings for the Aspeed Low Pin Count (LPC) Bus Controller
+======================================================================
+
+The LPC bus is a means to bridge a host CPU to a number of low-bandwidth
+peripheral devices, replacing the use of the ISA bus in the age of PCI[0]. The
+primary use case of the Aspeed LPC controller is as a slave on the bus
+(typically in a Baseboard Management Controller SoC), but under certain
+conditions it can also take the role of bus master.
+
+The LPC controller is represented as a multi-function device to account for the
+mix of functionality it provides. The principle split is between the register
+layout at the start of the I/O space which is, to quote the Aspeed datasheet,
+"basically compatible with the [LPC registers from the] popular BMC controller
+H8S/2168[1]", and everything else, where everything else is an eclectic
+collection of functions with a esoteric register layout. "Everything else",
+here labeled the "host" portion of the controller, includes, but is not limited
+to:
+
+* An IPMI Block Transfer[2] Controller
+
+* An LPC Host Controller: Manages LPC functions such as host vs slave mode, the
+ physical properties of some LPC pins, configuration of serial IRQs, and
+ APB-to-LPC bridging amonst other functions.
+
+* An LPC Host Interface Controller: Manages functions exposed to the host such
+ as LPC firmware hub cycles, configuration of the LPC-to-AHB mapping, UART
+ management and bus snoop configuration.
+
+* A set of SuperIO[3] scratch registers: Enables implementation of e.g. custom
+ hardware management protocols for handover between the host and baseboard
+ management controller.
+
+Additionally the state of the LPC controller influences the pinmux
+configuration, therefore the host portion of the controller is exposed as a
+syscon as a means to arbitrate access.
+
+[0] http://www.intel.com/design/chipsets/industry/25128901.pdf
+[1] https://www.renesas.com/en-sg/doc/products/mpumcu/001/rej09b0078_h8s2168.pdf?key=7c88837454702128622bee53acbda8f4
+[2] http://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf
+[3] https://en.wikipedia.org/wiki/Super_I/O
+
+Required properties
+===================
+
+- compatible: One of:
+ "aspeed,ast2400-lpc", "simple-mfd"
+ "aspeed,ast2500-lpc", "simple-mfd"
+
+- reg: contains the physical address and length values of the Aspeed
+ LPC memory region.
+
+- #address-cells: <1>
+- #size-cells: <1>
+- ranges: Maps 0 to the physical address and length of the LPC memory
+ region
+
+Required LPC Child nodes
+========================
+
+BMC Node
+--------
+
+- compatible: One of:
+ "aspeed,ast2400-lpc-bmc"
+ "aspeed,ast2500-lpc-bmc"
+
+- reg: contains the physical address and length values of the
+ H8S/2168-compatible LPC controller memory region
+
+Host Node
+---------
+
+- compatible: One of:
+ "aspeed,ast2400-lpc-host", "simple-mfd", "syscon"
+ "aspeed,ast2500-lpc-host", "simple-mfd", "syscon"
+
+- reg: contains the address and length values of the host-related
+ register space for the Aspeed LPC controller
+
+- #address-cells: <1>
+- #size-cells: <1>
+- ranges: Maps 0 to the address and length of the host-related LPC memory
+ region
+
+Example:
+
+lpc: lpc@1e789000 {
+ compatible = "aspeed,ast2500-lpc", "simple-mfd";
+ reg = <0x1e789000 0x1000>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e789000 0x1000>;
+
+ lpc_bmc: lpc-bmc@0 {
+ compatible = "aspeed,ast2500-lpc-bmc";
+ reg = <0x0 0x80>;
+ };
+
+ lpc_host: lpc-host@80 {
+ compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
+ reg = <0x80 0x1e0>;
+ reg-io-width = <4>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x80 0x1e0>;
+ };
+};
+
+Host Node Children
+==================
+
+LPC Host Controller
+-------------------
+
+The Aspeed LPC Host Controller configures the Low Pin Count (LPC) bus behaviour
+between the host and the baseboard management controller. The registers exist
+in the "host" portion of the Aspeed LPC controller, which must be the parent of
+the LPC host controller node.
+
+Required properties:
+
+- compatible: One of:
+ "aspeed,ast2400-lhc";
+ "aspeed,ast2500-lhc";
+
+- reg: contains offset/length values of the LHC memory regions. In the
+ AST2400 and AST2500 there are two regions.
+
+Example:
+
+lhc: lhc@20 {
+ compatible = "aspeed,ast2500-lhc";
+ reg = <0x20 0x24 0x48 0x8>;
+};
diff --git a/Documentation/devicetree/bindings/mfd/mfd.txt b/Documentation/devicetree/bindings/mfd/mfd.txt
index af9d6931a1a2..bcb6abb9d413 100644
--- a/Documentation/devicetree/bindings/mfd/mfd.txt
+++ b/Documentation/devicetree/bindings/mfd/mfd.txt
@@ -19,12 +19,22 @@ Optional properties:
- compatible : "simple-mfd" - this signifies that the operating system should
consider all subnodes of the MFD device as separate devices akin to how
- "simple-bus" inidicates when to see subnodes as children for a simple
+ "simple-bus" indicates when to see subnodes as children for a simple
memory-mapped bus. For more complex devices, when the nexus driver has to
probe registers to figure out what child devices exist etc, this should not
be used. In the latter case the child devices will be determined by the
operating system.
+- ranges: Describes the address mapping relationship to the parent. Should set
+ the child's base address to 0, the physical address within parent's address
+ space, and the length of the address map.
+
+- #address-cells: Specifies the number of cells used to represent physical base
+ addresses. Must be present if ranges is used.
+
+- #size-cells: Specifies the number of cells used to represent the size of an
+ address. Must be present if ranges is used.
+
Example:
foo@1000 {
diff --git a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
new file mode 100644
index 000000000000..15bc885f9df4
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
@@ -0,0 +1,31 @@
+Motorola CPCAP PMIC device tree binding
+
+Required properties:
+- compatible : One or both of "motorola,cpcap" or "ste,6556002"
+- reg : SPI chip select
+- interrupt-parent : The parent interrupt controller
+- interrupts : The interrupt line the device is connected to
+- interrupt-controller : Marks the device node as an interrupt controller
+- #interrupt-cells : The number of cells to describe an IRQ, should be 2
+- #address-cells : Child device offset number of cells, should be 1
+- #size-cells : Child device size number of cells, should be 0
+- spi-max-frequency : Typically set to 3000000
+- spi-cs-high : SPI chip select direction
+
+Example:
+
+&mcspi1 {
+ cpcap: pmic@0 {
+ compatible = "motorola,cpcap", "ste,6556002";
+ reg = <0>; /* cs0 */
+ interrupt-parent = <&gpio1>;
+ interrupts = <7 IRQ_TYPE_EDGE_RISING>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ spi-max-frequency = <3000000>;
+ spi-cs-high;
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt
index 949c85f8d02c..c568d52af5af 100644
--- a/Documentation/devicetree/bindings/mfd/mt6397.txt
+++ b/Documentation/devicetree/bindings/mfd/mt6397.txt
@@ -34,6 +34,10 @@ Optional subnodes:
- clk
Required properties:
- compatible: "mediatek,mt6397-clk"
+- led
+ Required properties:
+ - compatible: "mediatek,mt6323-led"
+ see Documentation/devicetree/bindings/leds/leds-mt6323.txt
Example:
pwrap: pwrap@1000f000 {
diff --git a/MAINTAINERS b/MAINTAINERS
index ad4f2ce991ca..7515e66945af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8476,6 +8476,7 @@ S: Supported
F: Documentation/devicetree/bindings/mfd/
F: drivers/mfd/
F: include/linux/mfd/
+F: include/dt-bindings/mfd/
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
M: Ulf Hansson <ulf.hansson@linaro.org>
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 25943e9bc8bf..604c7ade8df2 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -34,6 +34,8 @@
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
+#include <asm/unaligned.h>
+
/*
* @rows: Number of rows in the keypad
* @cols: Number of columns in the keypad
@@ -43,8 +45,9 @@
* @valid_keys: bitmap of existing keys for each matrix column
* @old_kb_state: bitmap of keys pressed last scan
* @dev: Device pointer
- * @idev: Input device
* @ec: Top level ChromeOS device to use to talk to EC
+ * @idev: The input device for the matrix keys.
+ * @bs_idev: The input device for non-matrix buttons and switches (or NULL).
* @notifier: interrupt event notifier for transport devices
*/
struct cros_ec_keyb {
@@ -57,12 +60,64 @@ struct cros_ec_keyb {
uint8_t *old_kb_state;
struct device *dev;
- struct input_dev *idev;
struct cros_ec_device *ec;
+
+ struct input_dev *idev;
+ struct input_dev *bs_idev;
struct notifier_block notifier;
};
+/**
+ * cros_ec_bs_map - Struct mapping Linux keycodes to EC button/switch bitmap
+ * #defines
+ *
+ * @ev_type: The type of the input event to generate (e.g., EV_KEY).
+ * @code: A linux keycode
+ * @bit: A #define like EC_MKBP_POWER_BUTTON or EC_MKBP_LID_OPEN
+ * @inverted: If the #define and EV_SW have opposite meanings, this is true.
+ * Only applicable to switches.
+ */
+struct cros_ec_bs_map {
+ unsigned int ev_type;
+ unsigned int code;
+ u8 bit;
+ bool inverted;
+};
+
+/* cros_ec_keyb_bs - Map EC button/switch #defines into kernel ones */
+static const struct cros_ec_bs_map cros_ec_keyb_bs[] = {
+ /* Buttons */
+ {
+ .ev_type = EV_KEY,
+ .code = KEY_POWER,
+ .bit = EC_MKBP_POWER_BUTTON,
+ },
+ {
+ .ev_type = EV_KEY,
+ .code = KEY_VOLUMEUP,
+ .bit = EC_MKBP_VOL_UP,
+ },
+ {
+ .ev_type = EV_KEY,
+ .code = KEY_VOLUMEDOWN,
+ .bit = EC_MKBP_VOL_DOWN,
+ },
+
+ /* Switches */
+ {
+ .ev_type = EV_SW,
+ .code = SW_LID,
+ .bit = EC_MKBP_LID_OPEN,
+ .inverted = true,
+ },
+ {
+ .ev_type = EV_SW,
+ .code = SW_TABLET_MODE,
+ .bit = EC_MKBP_TABLET_MODE,
+ },
+};
+
/*
* Returns true when there is at least one combination of pressed keys that
* results in ghosting.
@@ -149,20 +204,33 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
input_sync(ckdev->idev);
}
-static int cros_ec_keyb_open(struct input_dev *dev)
+/**
+ * cros_ec_keyb_report_bs - Report non-matrixed buttons or switches
+ *
+ * This takes a bitmap of buttons or switches from the EC and reports events,
+ * syncing at the end.
+ *
+ * @ckdev: The keyboard device.
+ * @ev_type: The input event type (e.g., EV_KEY).
+ * @mask: A bitmap of buttons from the EC.
+ */
+static void cros_ec_keyb_report_bs(struct cros_ec_keyb *ckdev,
+ unsigned int ev_type, u32 mask)
+
{
- struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+ struct input_dev *idev = ckdev->bs_idev;
+ int i;
- return blocking_notifier_chain_register(&ckdev->ec->event_notifier,
- &ckdev->notifier);
-}
+ for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) {
+ const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i];
-static void cros_ec_keyb_close(struct input_dev *dev)
-{
- struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+ if (map->ev_type != ev_type)
+ continue;
- blocking_notifier_chain_unregister(&ckdev->ec->event_notifier,
- &ckdev->notifier);
+ input_event(idev, ev_type, map->code,
+ !!(mask & BIT(map->bit)) ^ map->inverted);
+ }
+ input_sync(idev);
}
static int cros_ec_keyb_work(struct notifier_block *nb,
@@ -170,22 +238,54 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
{
struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
notifier);
+ u32 val;
+ unsigned int ev_type;
+
+ switch (ckdev->ec->event_data.event_type) {
+ case EC_MKBP_EVENT_KEY_MATRIX:
+ /*
+ * If EC is not the wake source, discard key state changes
+ * during suspend.
+ */
+ if (queued_during_suspend)
+ return NOTIFY_OK;
- if (ckdev->ec->event_data.event_type != EC_MKBP_EVENT_KEY_MATRIX)
+ if (ckdev->ec->event_size != ckdev->cols) {
+ dev_err(ckdev->dev,
+ "Discarded incomplete key matrix event.\n");
+ return NOTIFY_OK;
+ }
+ cros_ec_keyb_process(ckdev,
+ ckdev->ec->event_data.data.key_matrix,
+ ckdev->ec->event_size);
+ break;
+
+ case EC_MKBP_EVENT_BUTTON:
+ case EC_MKBP_EVENT_SWITCH:
+ /*
+ * If EC is not the wake source, discard key state
+ * changes during suspend. Switches will be re-checked in
+ * cros_ec_keyb_resume() to be sure nothing is lost.
+ */
+ if (queued_during_suspend)
+ return NOTIFY_OK;
+
+ if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
+ val = get_unaligned_le32(
+ &ckdev->ec->event_data.data.buttons);
+ ev_type = EV_KEY;
+ } else {
+ val = get_unaligned_le32(
+ &ckdev->ec->event_data.data.switches);
+ ev_type = EV_SW;
+ }
+ cros_ec_keyb_report_bs(ckdev, ev_type, val);
+ break;
+
+ default:
return NOTIFY_DONE;
- /*
- * If EC is not the wake source, discard key state changes during
- * suspend.
- */
- if (queued_during_suspend)
- return NOTIFY_OK;
- if (ckdev->ec->event_size != ckdev->cols) {
- dev_err(ckdev->dev,
- "Discarded incomplete key matrix event.\n");
- return NOTIFY_OK;
}
- cros_ec_keyb_process(ckdev, ckdev->ec->event_data.data.key_matrix,
- ckdev->ec->event_size);
+
return NOTIFY_OK;
}
@@ -213,22 +313,228 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev)
}
}
-static int cros_ec_keyb_probe(struct platform_device *pdev)
+/**
+ * cros_ec_keyb_info - Wrap the EC command EC_CMD_MKBP_INFO
+ *
+ * This wraps the EC_CMD_MKBP_INFO, abstracting out all of the marshalling and
+ * unmarshalling and different version nonsense into something simple.
+ *
+ * @ec_dev: The EC device
+ * @info_type: Either EC_MKBP_INFO_SUPPORTED or EC_MKBP_INFO_CURRENT.
+ * @event_type: Either EC_MKBP_EVENT_BUTTON or EC_MKBP_EVENT_SWITCH. Actually
+ * in some cases this could be EC_MKBP_EVENT_KEY_MATRIX or
+ * EC_MKBP_EVENT_HOST_EVENT too but we don't use in this driver.
+ * @result: Where we'll store the result; a union
+ * @result_size: The size of the result. Expected to be the size of one of
+ * the elements in the union.
+ *
+ * Returns 0 if no error or -error upon error.
+ */
+static int cros_ec_keyb_info(struct cros_ec_device *ec_dev,
+ enum ec_mkbp_info_type info_type,
+ enum ec_mkbp_event event_type,
+ union ec_response_get_next_data *result,
+ size_t result_size)
{
- struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
- struct device *dev = &pdev->dev;
- struct cros_ec_keyb *ckdev;
+ struct ec_params_mkbp_info *params;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kzalloc(sizeof(*msg) + max_t(size_t, result_size,
+ sizeof(*params)), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->command = EC_CMD_MKBP_INFO;
+ msg->version = 1;
+ msg->outsize = sizeof(*params);
+ msg->insize = result_size;
+ params = (struct ec_params_mkbp_info *)msg->data;
+ params->info_type = info_type;
+ params->event_type = event_type;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret < 0) {
+ dev_warn(ec_dev->dev, "Transfer error %d/%d: %d\n",
+ (int)info_type, (int)event_type, ret);
+ } else if (msg->result == EC_RES_INVALID_VERSION) {
+ /* With older ECs we just return 0 for everything */
+ memset(result, 0, result_size);
+ ret = 0;
+ } else if (msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec_dev->dev, "Error getting info %d/%d: %d\n",
+ (int)info_type, (int)event_type, msg->result);
+ ret = -EPROTO;
+ } else if (ret != result_size) {
+ dev_warn(ec_dev->dev, "Wrong size %d/%d: %d != %zu\n",
+ (int)info_type, (int)event_type,
+ ret, result_size);
+ ret = -EPROTO;
+ } else {
+ memcpy(result, msg->data, result_size);
+ ret = 0;
+ }
+
+ kfree(msg);
+
+ return ret;
+}
+
+/**
+ * cros_ec_keyb_query_switches - Query the state of switches and report
+ *
+ * This will ask the EC about the current state of switches and report to the
+ * kernel. Note that we don't query for buttons because they are more
+ * transitory and we'll get an update on the next release / press.
+ *
+ * @ckdev: The keyboard device
+ *
+ * Returns 0 if no error or -error upon error.
+ */
+static int cros_ec_keyb_query_switches(struct cros_ec_keyb *ckdev)
+{
+ struct cros_ec_device *ec_dev = ckdev->ec;
+ union ec_response_get_next_data event_data = {};
+ int ret;
+
+ ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_CURRENT,
+ EC_MKBP_EVENT_SWITCH, &event_data,
+ sizeof(event_data.switches));
+ if (ret)
+ return ret;
+
+ cros_ec_keyb_report_bs(ckdev, EV_SW,
+ get_unaligned_le32(&event_data.switches));
+
+ return 0;
+}
+
+/**
+ * cros_ec_keyb_resume - Resume the keyboard
+ *
+ * We use the resume notification as a chance to query the EC for switches.
+ *
+ * @dev: The keyboard device
+ *
+ * Returns 0 if no error or -error upon error.
+ */
+static __maybe_unused int cros_ec_keyb_resume(struct device *dev)
+{
+ struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
+
+ if (ckdev->bs_idev)
+ return cros_ec_keyb_query_switches(ckdev);
+
+ return 0;
+}
+
+/**
+ * cros_ec_keyb_register_bs - Register non-matrix buttons/switches
+ *
+ * Handles all the bits of the keyboard driver related to non-matrix buttons
+ * and switches, including asking the EC about which are present and telling
+ * the kernel to expect them.
+ *
+ * If this device has no support for buttons and switches we'll return no error
+ * but the ckdev->bs_idev will remain NULL when this function exits.
+ *
+ * @ckdev: The keyboard device
+ *
+ * Returns 0 if no error or -error upon error.
+ */
+static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev)
+{
+ struct cros_ec_device *ec_dev = ckdev->ec;
+ struct device *dev = ckdev->dev;
struct input_dev *idev;
- struct device_node *np;
- int err;
+ union ec_response_get_next_data event_data = {};
+ const char *phys;
+ u32 buttons;
+ u32 switches;
+ int ret;
+ int i;
+
+ ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED,
+ EC_MKBP_EVENT_BUTTON, &event_data,
+ sizeof(event_data.buttons));
+ if (ret)
+ return ret;
+ buttons = get_unaligned_le32(&event_data.buttons);
+
+ ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED,
+ EC_MKBP_EVENT_SWITCH, &event_data,
+ sizeof(event_data.switches));
+ if (ret)
+ return ret;
+ switches = get_unaligned_le32(&event_data.switches);
+
+ if (!buttons && !switches)
+ return 0;
- np = pdev->dev.of_node;
- if (!np)
- return -ENODEV;
+ /*
+ * We call the non-matrix buttons/switches 'input1', if present.
+ * Allocate phys before input dev, to ensure correct tear-down
+ * ordering.
+ */
+ phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input1", ec_dev->phys_name);
+ if (!phys)
+ return -ENOMEM;
- ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL);
- if (!ckdev)
+ idev = devm_input_allocate_device(dev);
+ if (!idev)
return -ENOMEM;
+
+ idev->name = "cros_ec_buttons";
+ idev->phys = phys;
+ __set_bit(EV_REP, idev->evbit);
+
+ idev->id.bustype = BUS_VIRTUAL;
+ idev->id.version = 1;
+ idev->id.product = 0;
+ idev->dev.parent = dev;
+
+ input_set_drvdata(idev, ckdev);
+ ckdev->bs_idev = idev;
+
+ for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) {
+ const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i];
+
+ if (buttons & BIT(map->bit))
+ input_set_capability(idev, map->ev_type, map->code);
+ }
+
+ ret = cros_ec_keyb_query_switches(ckdev);
+ if (ret) {
+ dev_err(dev, "cannot query switches\n");
+ return ret;
+ }
+
+ ret = input_register_device(ckdev->bs_idev);
+ if (ret) {
+ dev_err(dev, "cannot register input device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * cros_ec_keyb_register_bs - Register matrix keys
+ *
+ * Handles all the bits of the keyboard driver related to matrix keys.
+ *
+ * @ckdev: The keyboard device
+ *
+ * Returns 0 if no error or -error upon error.
+ */
+static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
+{
+ struct cros_ec_device *ec_dev = ckdev->ec;
+ struct device *dev = ckdev->dev;
+ struct input_dev *idev;
+ const char *phys;
+ int err;
+
err = matrix_keypad_parse_of_params(dev, &ckdev->rows, &ckdev->cols);
if (err)
return err;
@@ -241,27 +547,28 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
if (!ckdev->old_kb_state)
return -ENOMEM;
+ /*
+ * We call the keyboard matrix 'input0'. Allocate phys before input
+ * dev, to ensure correct tear-down ordering.
+ */
+ phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", ec_dev->phys_name);
+ if (!phys)
+ return -ENOMEM;
+
idev = devm_input_allocate_device(dev);
if (!idev)
return -ENOMEM;
- ckdev->ec = ec;
- ckdev->notifier.notifier_call = cros_ec_keyb_work;
- ckdev->dev = dev;
- dev_set_drvdata(dev, ckdev);
-
idev->name = CROS_EC_DEV_NAME;
- idev->phys = ec->phys_name;
+ idev->phys = phys;
__set_bit(EV_REP, idev->evbit);
idev->id.bustype = BUS_VIRTUAL;
idev->id.version = 1;
idev->id.product = 0;
idev->dev.parent = dev;
- idev->open = cros_ec_keyb_open;
- idev->close = cros_ec_keyb_close;
- ckdev->ghost_filter = of_property_read_bool(np,
+ ckdev->ghost_filter = of_property_read_bool(dev->of_node,
"google,needs-ghost-filter");
err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols,
@@ -287,6 +594,57 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
return 0;
}
+static int cros_ec_keyb_probe(struct platform_device *pdev)
+{
+ struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct cros_ec_keyb *ckdev;
+ int err;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL);
+ if (!ckdev)
+ return -ENOMEM;
+
+ ckdev->ec = ec;
+ ckdev->dev = dev;
+ dev_set_drvdata(dev, ckdev);
+
+ err = cros_ec_keyb_register_matrix(ckdev);
+ if (err) {
+ dev_err(dev, "cannot register matrix inputs: %d\n", err);
+ return err;
+ }
+
+ err = cros_ec_keyb_register_bs(ckdev);
+ if (err) {
+ dev_err(dev, "cannot register non-matrix inputs: %d\n", err);
+ return err;
+ }
+
+ ckdev->notifier.notifier_call = cros_ec_keyb_work;
+ err = blocking_notifier_chain_register(&ckdev->ec->event_notifier,
+ &ckdev->notifier);
+ if (err) {
+ dev_err(dev, "cannot register notifier: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int cros_ec_keyb_remove(struct platform_device *pdev)
+{
+ struct cros_ec_keyb *ckdev = dev_get_drvdata(&pdev->dev);
+
+ blocking_notifier_chain_unregister(&ckdev->ec->event_notifier,
+ &ckdev->notifier);
+
+ return 0;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id cros_ec_keyb_of_match[] = {
{ .compatible = "google,cros-ec-keyb" },
@@ -295,11 +653,15 @@ static const struct of_device_id cros_ec_keyb_of_match[] = {
MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match);
#endif
+static const SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume);
+
static struct platform_driver cros_ec_keyb_driver = {
.probe = cros_ec_keyb_probe,
+ .remove = cros_ec_keyb_remove,
.driver = {
.name = "cros-ec-keyb",
.of_match_table = of_match_ptr(cros_ec_keyb_of_match),
+ .pm = &cros_ec_keyb_pm_ops,
},
};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d0c14b88b24e..55ecdfb74d31 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -46,6 +46,7 @@ config MFD_SUN4I_GPADC
select REGMAP_MMIO
select REGMAP_IRQ
depends on ARCH_SUNXI || COMPILE_TEST
+ depends on !TOUCHSCREEN_SUN4I
help
Select this to get support for Allwinner SoCs (A10, A13 and A31) ADC.
This driver will only map the hardware interrupt and registers, you
@@ -506,17 +507,22 @@ config MFD_KEMPLD
device may provide functions like watchdog, GPIO, UART and I2C bus.
The following modules are supported:
+ * COMe-bBD#
* COMe-bBL6
* COMe-bHL6
+ * COMe-bSL6
* COMe-bIP#
+ * COMe-bKL6
* COMe-bPC2 (ETXexpress-PC)
* COMe-bSC# (ETXexpress-SC T#)
+ * COMe-cAL6
* COMe-cBL6
* COMe-cBT6
* COMe-cBW6
* COMe-cCT6
* COMe-cDC2 (microETXexpress-DC)
* COMe-cHL6
+ * COMe-cKL6
* COMe-cPC2 (microETXexpress-PC)
* COMe-cSL6
* COMe-mAL10
@@ -714,6 +720,17 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
+config MFD_CPCAP
+ tristate "Support for Motorola CPCAP"
+ depends on SPI
+ depends on OF || COMPILE_TEST
+ select REGMAP_SPI
+ select REGMAP_IRQ
+ help
+ Say yes here if you want to include driver for CPCAP.
+ It is used on many Motorola phones and tablets as a PMIC.
+ At least Motorola Droid 4 is known to use CPCAP.
+
config MFD_VIPERBOARD
tristate "Nano River Technologies Viperboard"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 876ca8600c51..31ce07611a6f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
+obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o
obj-$(CONFIG_MCP) += mcp-core.o
obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 6e00124cef01..8511c068a610 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -656,8 +656,8 @@ static const struct mfd_cell ab8500_devs[] = {
.of_compatible = "stericsson,ab8500-regulator",
},
{
- .name = "abx500-clk",
- .of_compatible = "stericsson,abx500-clk",
+ .name = "ab8500-clk",
+ .of_compatible = "stericsson,ab8500-clk",
},
{
.name = "ab8500-gpadc",
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 80c0efa66ac1..5b0a0850ef69 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -101,7 +101,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
u8 bank;
if (sysctrl_dev == NULL)
- return -EINVAL;
+ return -EPROBE_DEFER;
bank = (reg >> 8);
if (!valid_bank(bank))
@@ -117,11 +117,13 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
u8 bank;
if (sysctrl_dev == NULL)
- return -EINVAL;
+ return -EPROBE_DEFER;
bank = (reg >> 8);
- if (!valid_bank(bank))
+ if (!valid_bank(bank)) {
+ pr_err("invalid bank\n");
return -EINVAL;
+ }
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
@@ -148,9 +150,15 @@ static int ab8500_sysctrl_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ab8500_sysctrl_match[] = {
+ { .compatible = "stericsson,ab8500-sysctrl", },
+ {}
+};
+
static struct platform_driver ab8500_sysctrl_driver = {
.driver = {
.name = "ab8500-sysctrl",
+ .of_match_table = ab8500_sysctrl_match,
},
.probe = ab8500_sysctrl_probe,
.remove = ab8500_sysctrl_remove,
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index 2e01975f042d..09cf3699e354 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -26,6 +26,9 @@
#include "arizona.h"
+#define ARIZONA_AOD_IRQ_INDEX 0
+#define ARIZONA_MAIN_IRQ_INDEX 1
+
static int arizona_map_irq(struct arizona *arizona, int irq)
{
int ret;
@@ -204,9 +207,10 @@ static const struct irq_domain_ops arizona_domain_ops = {
int arizona_irq_init(struct arizona *arizona)
{
int flags = IRQF_ONESHOT;
- int ret, i;
+ int ret;
const struct regmap_irq_chip *aod, *irq;
struct irq_data *irq_data;
+ unsigned int virq;
arizona->ctrlif_error = true;
@@ -318,24 +322,34 @@ int arizona_irq_init(struct arizona *arizona)
}
if (aod) {
- ret = regmap_add_irq_chip(arizona->regmap,
- irq_create_mapping(arizona->virq, 0),
- IRQF_ONESHOT, 0, aod,
- &arizona->aod_irq_chip);
+ virq = irq_create_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
+ if (!virq) {
+ dev_err(arizona->dev, "Failed to map AOD IRQs\n");
+ ret = -EINVAL;
+ goto err_domain;
+ }
+
+ ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
+ 0, aod, &arizona->aod_irq_chip);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to add AOD IRQs: %d\n", ret);
- goto err;
+ goto err_map_aod;
}
}
- ret = regmap_add_irq_chip(arizona->regmap,
- irq_create_mapping(arizona->virq, 1),
- IRQF_ONESHOT, 0, irq,
- &arizona->irq_chip);
+ virq = irq_create_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
+ if (!virq) {
+ dev_err(arizona->dev, "Failed to map main IRQs\n");
+ ret = -EINVAL;
+ goto err_aod;
+ }
+
+ ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
+ 0, irq, &arizona->irq_chip);
if (ret != 0) {
dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);
- goto err_aod;
+ goto err_map_main_irq;
}
/* Used to emulate edge trigger and to work around broken pinmux */
@@ -368,9 +382,8 @@ int arizona_irq_init(struct arizona *arizona)
}
/* Make sure the boot done IRQ is unmasked for resumes */
- i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
- ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
- "Boot done", arizona);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_BOOT_DONE, "Boot done",
+ arizona_boot_done, arizona);
if (ret != 0) {
dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
arizona->irq, ret);
@@ -379,10 +392,9 @@ int arizona_irq_init(struct arizona *arizona)
/* Handle control interface errors in the core */
if (arizona->ctrlif_error) {
- i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
- ret = request_threaded_irq(i, NULL, arizona_ctrlif_err,
- IRQF_ONESHOT,
- "Control interface error", arizona);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR,
+ "Control interface error",
+ arizona_ctrlif_err, arizona);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to request CTRLIF_ERR %d: %d\n",
@@ -394,29 +406,47 @@ int arizona_irq_init(struct arizona *arizona)
return 0;
err_ctrlif:
- free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
err_boot_done:
free_irq(arizona->irq, arizona);
err_main_irq:
- regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1),
+ regmap_del_irq_chip(irq_find_mapping(arizona->virq,
+ ARIZONA_MAIN_IRQ_INDEX),
arizona->irq_chip);
+err_map_main_irq:
+ irq_dispose_mapping(irq_find_mapping(arizona->virq,
+ ARIZONA_MAIN_IRQ_INDEX));
err_aod:
- regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0),
+ regmap_del_irq_chip(irq_find_mapping(arizona->virq,
+ ARIZONA_AOD_IRQ_INDEX),
arizona->aod_irq_chip);
+err_map_aod:
+ irq_dispose_mapping(irq_find_mapping(arizona->virq,
+ ARIZONA_AOD_IRQ_INDEX));
+err_domain:
+ irq_domain_remove(arizona->virq);
err:
return ret;
}
int arizona_irq_exit(struct arizona *arizona)
{
+ unsigned int virq;
+
if (arizona->ctrlif_error)
- free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR),
- arizona);
- free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
- regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1),
- arizona->irq_chip);
- regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0),
- arizona->aod_irq_chip);
+ arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
+
+ virq = irq_find_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
+ regmap_del_irq_chip(virq, arizona->irq_chip);
+ irq_dispose_mapping(virq);
+
+ virq = irq_find_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
+ regmap_del_irq_chip(virq, arizona->aod_irq_chip);
+ irq_dispose_mapping(virq);
+
+ irq_domain_remove(arizona->virq);
+
free_irq(arizona->irq, arizona);
return 0;
diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
index 198e9cea77f9..a0bddc5bd043 100644
--- a/drivers/mfd/arizona.h
+++ b/drivers/mfd/arizona.h
@@ -17,8 +17,6 @@
#include <linux/regmap.h>
#include <linux/pm.h>
-struct wm_arizona;
-
extern const struct regmap_config wm5102_i2c_regmap;
extern const struct regmap_config wm5102_spi_regmap;
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index ed918de84238..25115fe2acdf 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -31,6 +31,8 @@
#define AXP20X_OFF 0x80
+#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
+
static const char * const axp20x_model_names[] = {
"AXP152",
"AXP202",
@@ -118,7 +120,14 @@ static const struct regmap_range axp288_writeable_ranges[] = {
};
static const struct regmap_range axp288_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON),
+ regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL),
+ regmap_reg_range(AXP288_BC_DET_STAT, AXP288_BC_DET_STAT),
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L),
+ regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL),
+ regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE),
+ regmap_reg_range(AXP288_RT_BATT_V_H, AXP288_RT_BATT_V_L),
+ regmap_reg_range(AXP20X_FG_RES, AXP288_FG_CC_CAP_REG),
};
static const struct regmap_access_table axp288_writeable_table = {
@@ -207,14 +216,14 @@ static struct resource axp22x_pek_resources[] = {
static struct resource axp288_power_button_resources[] = {
{
.name = "PEK_DBR",
- .start = AXP288_IRQ_POKN,
- .end = AXP288_IRQ_POKN,
+ .start = AXP288_IRQ_POKP,
+ .end = AXP288_IRQ_POKP,
.flags = IORESOURCE_IRQ,
},
{
.name = "PEK_DBF",
- .start = AXP288_IRQ_POKP,
- .end = AXP288_IRQ_POKP,
+ .start = AXP288_IRQ_POKN,
+ .end = AXP288_IRQ_POKN,
.flags = IORESOURCE_IRQ,
},
};
@@ -407,6 +416,9 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3),
INIT_REGMAP_IRQ(AXP288, OV, 0, 4),
+ INIT_REGMAP_IRQ(AXP288, FALLING_ALT, 0, 5),
+ INIT_REGMAP_IRQ(AXP288, RISING_ALT, 0, 6),
+ INIT_REGMAP_IRQ(AXP288, OV_ALT, 0, 7),
INIT_REGMAP_IRQ(AXP288, DONE, 1, 2),
INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3),
@@ -589,7 +601,22 @@ static struct mfd_cell axp20x_cells[] = {
},
};
-static struct mfd_cell axp22x_cells[] = {
+static struct mfd_cell axp221_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp22x_pek_resources),
+ .resources = axp22x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
+ }, {
+ .name = "axp20x-usb-power-supply",
+ .of_compatible = "x-powers,axp221-usb-power-supply",
+ .num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
+ .resources = axp22x_usb_power_supply_resources,
+ },
+};
+
+static struct mfd_cell axp223_cells[] = {
{
.name = "axp20x-pek",
.num_resources = ARRAY_SIZE(axp22x_pek_resources),
@@ -598,7 +625,7 @@ static struct mfd_cell axp22x_cells[] = {
.name = "axp20x-regulator",
}, {
.name = "axp20x-usb-power-supply",
- .of_compatible = "x-powers,axp221-usb-power-supply",
+ .of_compatible = "x-powers,axp223-usb-power-supply",
.num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
.resources = axp22x_usb_power_supply_resources,
},
@@ -791,9 +818,14 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
break;
case AXP221_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp221_cells);
+ axp20x->cells = axp221_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
+ break;
case AXP223_ID:
- axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
- axp20x->cells = axp22x_cells;
+ axp20x->nr_cells = ARRAY_SIZE(axp223_cells);
+ axp20x->cells = axp223_cells;
axp20x->regmap_cfg = &axp22x_regmap_config;
axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
break;
@@ -802,6 +834,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
axp20x->regmap_cfg = &axp288_regmap_config;
axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
+ axp20x->irq_flags = IRQF_TRIGGER_LOW;
break;
case AXP806_ID:
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
@@ -830,10 +863,33 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
{
int ret;
+ /*
+ * The AXP806 supports either master/standalone or slave mode.
+ * Slave mode allows sharing the serial bus, even with multiple
+ * AXP806 which all have the same hardware address.
+ *
+ * This is done with extra "serial interface address extension",
+ * or AXP806_BUS_ADDR_EXT, and "register address extension", or
+ * AXP806_REG_ADDR_EXT, registers. The former is read-only, with
+ * 1 bit customizable at the factory, and 1 bit depending on the
+ * state of an external pin. The latter is writable. The device
+ * will only respond to operations to its other registers when
+ * the these device addressing bits (in the upper 4 bits of the
+ * registers) match.
+ *
+ * Since we only support an AXP806 chained to an AXP809 in slave
+ * mode, and there isn't any existing hardware which uses AXP806
+ * in master mode, or has 2 AXP806s in the same system, we can
+ * just program the register address extension to the slave mode
+ * address.
+ */
+ if (axp20x->variant == AXP806_ID)
+ regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
+ AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
+
ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
- IRQF_ONESHOT | IRQF_SHARED, -1,
- axp20x->regmap_irq_chip,
- &axp20x->regmap_irqc);
+ IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags,
+ -1, axp20x->regmap_irq_chip, &axp20x->regmap_irqc);
if (ret) {
dev_err(axp20x->dev, "failed to add irq chip: %d\n", ret);
return ret;
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index abd83424b498..9b66a98ba4bf 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>
+#include <linux/suspend.h>
#include <asm/unaligned.h>
#define CROS_EC_DEV_EC_INDEX 0
@@ -65,6 +66,24 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
return IRQ_HANDLED;
}
+static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
+{
+ struct {
+ struct cros_ec_command msg;
+ struct ec_params_host_sleep_event req;
+ } __packed buf;
+
+ memset(&buf, 0, sizeof(buf));
+
+ buf.req.sleep_event = sleep_event;
+
+ buf.msg.command = EC_CMD_HOST_SLEEP_EVENT;
+ buf.msg.version = 0;
+ buf.msg.outsize = sizeof(buf.req);
+
+ return cros_ec_cmd_xfer(ec_dev, &buf.msg);
+}
+
int cros_ec_register(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
@@ -136,6 +155,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
}
}
+ /*
+ * Clear sleep event - this will fail harmlessly on platforms that
+ * don't implement the sleep event host command.
+ */
+ err = cros_ec_sleep_event(ec_dev, 0);
+ if (err < 0)
+ dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
+ err);
+
dev_info(dev, "Chrome EC device registered\n");
return 0;
@@ -159,12 +187,24 @@ EXPORT_SYMBOL(cros_ec_remove);
int cros_ec_suspend(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
+ int ret;
+ u8 sleep_event;
+
+ sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
+ HOST_SLEEP_EVENT_S3_RESUME :
+ HOST_SLEEP_EVENT_S0IX_RESUME;
+
+ ret = cros_ec_sleep_event(ec_dev, sleep_event);
+ if (ret < 0)
+ dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec",
+ ret);
if (device_may_wakeup(dev))
ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
disable_irq(ec_dev->irq);
ec_dev->was_wake_device = ec_dev->wake_enabled;
+ ec_dev->suspended = true;
return 0;
}
@@ -179,8 +219,21 @@ static void cros_ec_drain_events(struct cros_ec_device *ec_dev)
int cros_ec_resume(struct cros_ec_device *ec_dev)
{
+ int ret;
+ u8 sleep_event;
+
+ ec_dev->suspended = false;
enable_irq(ec_dev->irq);
+ sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ?
+ HOST_SLEEP_EVENT_S3_RESUME :
+ HOST_SLEEP_EVENT_S0IX_RESUME;
+
+ ret = cros_ec_sleep_event(ec_dev, sleep_event);
+ if (ret < 0)
+ dev_dbg(ec_dev->dev, "Error %d sending resume event to ec",
+ ret);
+
/*
* In some cases, we need to distinguish between events that occur
* during suspend if the EC is not a wake source. For example,
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 78dbcf8b0bef..16ffeaeb1385 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -157,7 +157,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info },
-
+ /* GLK */
+ { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index da5722d7c540..895f655780a7 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -496,6 +496,14 @@ static struct platform_driver kempld_driver = {
static struct dmi_system_id kempld_dmi_table[] __initdata = {
{
+ .ident = "BBD6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "BBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -512,6 +520,30 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "BKL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "BSL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CAL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "CBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -600,6 +632,14 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "CKL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "CNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index be42957a78e1..d98a5d974092 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* This driver supports the following I/O Controller hubs:
* (See the intel documentation on http://developer.intel.com.)
* document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
@@ -45,18 +41,6 @@
* document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
* document number 320066-003, 320257-008: EP80597 (IICH)
* document number 324645-001, 324646-001: Cougar Point (CPT)
- * document number TBD : Patsburg (PBG)
- * document number TBD : DH89xxCC
- * document number TBD : Panther Point
- * document number TBD : Lynx Point
- * document number TBD : Lynx Point-LP
- * document number TBD : Wellsburg
- * document number TBD : Avoton SoC
- * document number TBD : Coleto Creek
- * document number TBD : Wildcat Point-LP
- * document number TBD : 9 Series
- * document number TBD : Lewisburg
- * document number TBD : Apollo Lake SoC
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -567,6 +551,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
},
[LPC_APL] = {
.name = "Apollo Lake SoC",
+ .iTCO_version = 5,
.spi_type = INTEL_SPI_BXT,
},
};
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index 7b68ed72e9cb..b0e8e13c0049 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -34,6 +34,7 @@
#include <linux/mfd/max77686-private.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/of_device.h>
static const struct mfd_cell max77686_devs[] = {
{ .name = "max77686-pmic", },
@@ -171,11 +172,9 @@ static const struct of_device_id max77686_pmic_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, max77686_pmic_dt_match);
-static int max77686_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int max77686_i2c_probe(struct i2c_client *i2c)
{
struct max77686_dev *max77686 = NULL;
- const struct of_device_id *match;
unsigned int data;
int ret = 0;
const struct regmap_config *config;
@@ -188,16 +187,8 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
if (!max77686)
return -ENOMEM;
- if (i2c->dev.of_node) {
- match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node);
- if (!match)
- return -EINVAL;
-
- max77686->type = (unsigned long)match->data;
- } else
- max77686->type = id->driver_data;
-
i2c_set_clientdata(i2c, max77686);
+ max77686->type = (unsigned long)of_device_get_match_data(&i2c->dev);
max77686->dev = &i2c->dev;
max77686->i2c = i2c;
@@ -250,13 +241,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static const struct i2c_device_id max77686_i2c_id[] = {
- { "max77686", TYPE_MAX77686 },
- { "max77802", TYPE_MAX77802 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
-
#ifdef CONFIG_PM_SLEEP
static int max77686_suspend(struct device *dev)
{
@@ -302,8 +286,7 @@ static struct i2c_driver max77686_i2c_driver = {
.pm = &max77686_pm,
.of_match_table = of_match_ptr(max77686_pmic_dt_match),
},
- .probe = max77686_i2c_probe,
- .id_table = max77686_i2c_id,
+ .probe_new = max77686_i2c_probe,
};
module_i2c_driver(max77686_i2c_driver);
diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
new file mode 100644
index 000000000000..6aeada7d7ce5
--- /dev/null
+++ b/drivers/mfd/motorola-cpcap.c
@@ -0,0 +1,259 @@
+/*
+ * Motorola CPCAP PMIC core driver
+ *
+ * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+
+#include <linux/mfd/motorola-cpcap.h>
+#include <linux/spi/spi.h>
+
+#define CPCAP_NR_IRQ_REG_BANKS 6
+#define CPCAP_NR_IRQ_CHIPS 3
+
+struct cpcap_ddata {
+ struct spi_device *spi;
+ struct regmap_irq *irqs;
+ struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
+ const struct regmap_config *regmap_conf;
+ struct regmap *regmap;
+};
+
+static int cpcap_check_revision(struct cpcap_ddata *cpcap)
+{
+ u16 vendor, rev;
+ int ret;
+
+ ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor);
+ if (ret)
+ return ret;
+
+ ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev);
+ if (ret)
+ return ret;
+
+ dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
+ vendor == CPCAP_VENDOR_ST ? "ST" : "TI",
+ CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
+ rev);
+
+ if (rev < CPCAP_REVISION_2_1) {
+ dev_info(&cpcap->spi->dev,
+ "Please add old CPCAP revision support as needed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * First two irq chips are the two private macro interrupt chips, the third
+ * irq chip is for register banks 1 - 4 and is available for drivers to use.
+ */
+static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
+ {
+ .name = "cpcap-m2",
+ .num_regs = 1,
+ .status_base = CPCAP_REG_MI1,
+ .ack_base = CPCAP_REG_MI1,
+ .mask_base = CPCAP_REG_MIM1,
+ .use_ack = true,
+ },
+ {
+ .name = "cpcap-m2",
+ .num_regs = 1,
+ .status_base = CPCAP_REG_MI2,
+ .ack_base = CPCAP_REG_MI2,
+ .mask_base = CPCAP_REG_MIM2,
+ .use_ack = true,
+ },
+ {
+ .name = "cpcap1-4",
+ .num_regs = 4,
+ .status_base = CPCAP_REG_INT1,
+ .ack_base = CPCAP_REG_INT1,
+ .mask_base = CPCAP_REG_INTM1,
+ .type_base = CPCAP_REG_INTS1,
+ .use_ack = true,
+ },
+};
+
+static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap,
+ struct regmap_irq *rirq,
+ int irq_base, int irq)
+{
+ unsigned int reg_offset;
+ unsigned int bit, mask;
+
+ reg_offset = irq - irq_base;
+ reg_offset /= cpcap->regmap_conf->val_bits;
+ reg_offset *= cpcap->regmap_conf->reg_stride;
+
+ bit = irq % cpcap->regmap_conf->val_bits;
+ mask = (1 << bit);
+
+ rirq->reg_offset = reg_offset;
+ rirq->mask = mask;
+}
+
+static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
+ int irq_start, int nr_irqs)
+{
+ struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip];
+ int i, ret;
+
+ for (i = irq_start; i < irq_start + nr_irqs; i++) {
+ struct regmap_irq *rirq = &cpcap->irqs[i];
+
+ cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i);
+ }
+ chip->irqs = &cpcap->irqs[irq_start];
+ chip->num_irqs = nr_irqs;
+ chip->irq_drv_data = cpcap;
+
+ ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
+ cpcap->spi->irq,
+ IRQF_TRIGGER_RISING |
+ IRQF_SHARED, -1,
+ chip, &cpcap->irqdata[irq_chip]);
+ if (ret) {
+ dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n",
+ irq_chip, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cpcap_init_irq(struct cpcap_ddata *cpcap)
+{
+ int ret;
+
+ cpcap->irqs = devm_kzalloc(&cpcap->spi->dev,
+ sizeof(*cpcap->irqs) *
+ CPCAP_NR_IRQ_REG_BANKS *
+ cpcap->regmap_conf->val_bits,
+ GFP_KERNEL);
+ if (!cpcap->irqs)
+ return -ENOMEM;
+
+ ret = cpcap_init_irq_chip(cpcap, 0, 0, 16);
+ if (ret)
+ return ret;
+
+ ret = cpcap_init_irq_chip(cpcap, 1, 16, 16);
+ if (ret)
+ return ret;
+
+ ret = cpcap_init_irq_chip(cpcap, 2, 32, 64);
+ if (ret)
+ return ret;
+
+ enable_irq_wake(cpcap->spi->irq);
+
+ return 0;
+}
+
+static const struct of_device_id cpcap_of_match[] = {
+ { .compatible = "motorola,cpcap", },
+ { .compatible = "st,6556002", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_of_match);
+
+static const struct regmap_config cpcap_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 4,
+ .pad_bits = 0,
+ .val_bits = 16,
+ .write_flag_mask = 0x8000,
+ .max_register = CPCAP_REG_ST_TEST2,
+ .cache_type = REGCACHE_NONE,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int cpcap_probe(struct spi_device *spi)
+{
+ const struct of_device_id *match;
+ struct cpcap_ddata *cpcap;
+ int ret;
+
+ match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
+ if (!match)
+ return -ENODEV;
+
+ cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap)
+ return -ENOMEM;
+
+ cpcap->spi = spi;
+ spi_set_drvdata(spi, cpcap);
+
+ spi->bits_per_word = 16;
+ spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ cpcap->regmap_conf = &cpcap_regmap_config;
+ cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
+ if (IS_ERR(cpcap->regmap)) {
+ ret = PTR_ERR(cpcap->regmap);
+ dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n",
+ ret);
+
+ return ret;
+ }
+
+ ret = cpcap_check_revision(cpcap);
+ if (ret) {
+ dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret);
+ return ret;
+ }
+
+ ret = cpcap_init_irq(cpcap);
+ if (ret)
+ return ret;
+
+ return of_platform_populate(spi->dev.of_node, NULL, NULL,
+ &cpcap->spi->dev);
+}
+
+static int cpcap_remove(struct spi_device *pdev)
+{
+ struct cpcap_ddata *cpcap = spi_get_drvdata(pdev);
+
+ of_platform_depopulate(&cpcap->spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver cpcap_driver = {
+ .driver = {
+ .name = "cpcap-core",
+ .of_match_table = cpcap_of_match,
+ },
+ .probe = cpcap_probe,
+ .remove = cpcap_remove,
+};
+module_spi_driver(cpcap_driver);
+
+MODULE_ALIAS("platform:cpcap");
+MODULE_DESCRIPTION("CPCAP driver");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index e14d8b058f0c..8e601c846d08 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -48,6 +48,10 @@ static const struct mfd_cell mt6323_devs[] = {
.name = "mt6323-regulator",
.of_compatible = "mediatek,mt6323-regulator"
},
+ {
+ .name = "mt6323-led",
+ .of_compatible = "mediatek,mt6323-led"
+ },
};
static const struct mfd_cell mt6397_devs[] = {
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index 2c9acdba7c2d..fd087cbb0bde 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -247,7 +247,7 @@ static const struct regmap_irq rk818_irqs[] = {
},
};
-static struct regmap_irq_chip rk808_irq_chip = {
+static const struct regmap_irq_chip rk808_irq_chip = {
.name = "rk808",
.irqs = rk808_irqs,
.num_irqs = ARRAY_SIZE(rk808_irqs),
@@ -259,7 +259,7 @@ static struct regmap_irq_chip rk808_irq_chip = {
.init_ack_masked = true,
};
-static struct regmap_irq_chip rk818_irq_chip = {
+static const struct regmap_irq_chip rk818_irq_chip = {
.name = "rk818",
.irqs = rk818_irqs,
.num_irqs = ARRAY_SIZE(rk818_irqs),
diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c
index 011fcc555945..2b658bed47db 100644
--- a/drivers/mfd/sun6i-prcm.c
+++ b/drivers/mfd/sun6i-prcm.c
@@ -12,6 +12,9 @@
#include <linux/init.h>
#include <linux/of.h>
+#define SUN8I_CODEC_ANALOG_BASE 0x1c0
+#define SUN8I_CODEC_ANALOG_SIZE 0x4
+
struct prcm_data {
int nsubdevs;
const struct mfd_cell *subdevs;
@@ -57,6 +60,10 @@ static const struct resource sun6i_a31_apb0_rstc_res[] = {
},
};
+static const struct resource sun8i_codec_analog_res[] = {
+ DEFINE_RES_MEM(SUN8I_CODEC_ANALOG_BASE, SUN8I_CODEC_ANALOG_SIZE),
+};
+
static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
{
.name = "sun6i-a31-ar100-clk",
@@ -109,6 +116,12 @@ static const struct mfd_cell sun8i_a23_prcm_subdevs[] = {
.num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
.resources = sun6i_a31_apb0_rstc_res,
},
+ {
+ .name = "sun8i-codec-analog",
+ .of_compatible = "allwinner,sun8i-a23-codec-analog",
+ .num_resources = ARRAY_SIZE(sun8i_codec_analog_res),
+ .resources = sun8i_codec_analog_res,
+ },
};
static const struct prcm_data sun6i_a31_prcm_data = {
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
index 45871403f995..785d19f6f7c9 100644
--- a/drivers/mfd/tps65912-i2c.c
+++ b/drivers/mfd/tps65912-i2c.c
@@ -27,6 +27,7 @@ static const struct of_device_id tps65912_i2c_of_match_table[] = {
{ .compatible = "ti,tps65912", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, tps65912_i2c_of_match_table);
static int tps65912_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 04053fe1e980..ed5dee744c74 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -447,6 +447,11 @@ static int get_next_event(struct cros_ec_device *ec_dev)
struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
int ret;
+ if (ec_dev->suspended) {
+ dev_dbg(ec_dev->dev, "Device suspended.\n");
+ return -EHOSTDOWN;
+ }
+
msg->version = 0;
msg->command = EC_CMD_GET_NEXT_EVENT;
msg->insize = sizeof(ec_dev->event_data);
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 552cc1d61cc7..44412c9d26e1 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -45,7 +45,7 @@ enum abx500_adc_therm {
* struct abx500_res_to_temp - defines one point in a temp to res curve. To
* be used in battery packs that combines the identification resistor with a
* NTC resistor.
- * @temp: battery pack temperature in Celcius
+ * @temp: battery pack temperature in Celsius
* @resist: NTC resistor net total resistance
*/
struct abx500_res_to_temp {
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 12a5b396921e..e63681eb6c62 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -279,7 +279,7 @@ enum bup_vch_sel {
* struct res_to_temp - defines one point in a temp to res curve. To
* be used in battery packs that combines the identification resistor with a
* NTC resistor.
- * @temp: battery pack temperature in Celcius
+ * @temp: battery pack temperature in Celsius
* @resist: NTC resistor net total resistance
*/
struct res_to_temp {
@@ -290,7 +290,7 @@ struct res_to_temp {
/**
* struct batres_vs_temp - defines one point in a temp vs battery internal
* resistance curve.
- * @temp: battery pack temperature in Celcius
+ * @temp: battery pack temperature in Celsius
* @resist: battery internal reistance in mOhm
*/
struct batres_vs_temp {
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index f848ee86a339..0d9a1ff38393 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -235,10 +235,20 @@ enum axp20x_variants {
#define AXP22X_BATLOW_THRES1 0xe6
/* AXP288 specific registers */
+#define AXP288_POWER_REASON 0x02
+#define AXP288_BC_GLOBAL 0x2c
+#define AXP288_BC_VBUS_CNTL 0x2d
+#define AXP288_BC_USB_STAT 0x2e
+#define AXP288_BC_DET_STAT 0x2f
#define AXP288_PMIC_ADC_H 0x56
#define AXP288_PMIC_ADC_L 0x57
+#define AXP288_TS_ADC_H 0x58
+#define AXP288_TS_ADC_L 0x59
+#define AXP288_GP_ADC_H 0x5a
+#define AXP288_GP_ADC_L 0x5b
#define AXP288_ADC_TS_PIN_CTRL 0x84
-#define AXP288_PMIC_ADC_EN 0x84
+#define AXP288_RT_BATT_V_H 0xa0
+#define AXP288_RT_BATT_V_L 0xa1
/* Fuel Gauge */
#define AXP288_FG_RDC1_REG 0xba
@@ -515,14 +525,10 @@ enum axp809_irqs {
AXP809_IRQ_GPIO0_INPUT,
};
-#define AXP288_TS_ADC_H 0x58
-#define AXP288_TS_ADC_L 0x59
-#define AXP288_GP_ADC_H 0x5a
-#define AXP288_GP_ADC_L 0x5b
-
struct axp20x_dev {
struct device *dev;
int irq;
+ unsigned long irq_flags;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
long variant;
@@ -582,7 +588,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x);
int axp20x_device_probe(struct axp20x_dev *axp20x);
/**
- * axp20x_device_probe(): Remove a axp20x device
+ * axp20x_device_remove(): Remove a axp20x device
*
* @axp20x: axp20x device to remove
*
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index f62043a75f43..7a01c94496f1 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -103,6 +103,7 @@ struct cros_ec_command {
* @din_size: size of din buffer to allocate (zero to use static din)
* @dout_size: size of dout buffer to allocate (zero to use static dout)
* @wake_enabled: true if this device can wake the system from sleep
+ * @suspended: true if this device had been suspended
* @cmd_xfer: send command to EC and get response
* Returns the number of bytes received if the communication succeeded, but
* that doesn't mean the EC was happy with the command. The caller
@@ -136,6 +137,7 @@ struct cros_ec_device {
int din_size;
int dout_size;
bool wake_enabled;
+ bool suspended;
int (*cmd_xfer)(struct cros_ec_device *ec,
struct cros_ec_command *msg);
int (*pkt_xfer)(struct cros_ec_device *ec,
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 098c3501ad2c..f1ef6388c233 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -1840,18 +1840,69 @@ struct ec_response_tmp006_get_raw {
*
* Returns raw data for keyboard cols; see ec_response_mkbp_info.cols for
* expected response size.
+ *
+ * NOTE: This has been superseded by EC_CMD_MKBP_GET_NEXT_EVENT. If you wish
+ * to obtain the instantaneous state, use EC_CMD_MKBP_INFO with the type
+ * EC_MKBP_INFO_CURRENT and event EC_MKBP_EVENT_KEY_MATRIX.
*/
#define EC_CMD_MKBP_STATE 0x60
-/* Provide information about the matrix : number of rows and columns */
+/*
+ * Provide information about various MKBP things. See enum ec_mkbp_info_type.
+ */
#define EC_CMD_MKBP_INFO 0x61
struct ec_response_mkbp_info {
uint32_t rows;
uint32_t cols;
- uint8_t switches;
+ /* Formerly "switches", which was 0. */
+ uint8_t reserved;
} __packed;
+struct ec_params_mkbp_info {
+ uint8_t info_type;
+ uint8_t event_type;
+} __packed;
+
+enum ec_mkbp_info_type {
+ /*
+ * Info about the keyboard matrix: number of rows and columns.
+ *
+ * Returns struct ec_response_mkbp_info.
+ */
+ EC_MKBP_INFO_KBD = 0,
+
+ /*
+ * For buttons and switches, info about which specifically are
+ * supported. event_type must be set to one of the values in enum
+ * ec_mkbp_event.
+ *
+ * For EC_MKBP_EVENT_BUTTON and EC_MKBP_EVENT_SWITCH, returns a 4 byte
+ * bitmask indicating which buttons or switches are present. See the
+ * bit inidices below.
+ */
+ EC_MKBP_INFO_SUPPORTED = 1,
+
+ /*
+ * Instantaneous state of buttons and switches.
+ *
+ * event_type must be set to one of the values in enum ec_mkbp_event.
+ *
+ * For EC_MKBP_EVENT_KEY_MATRIX, returns uint8_t key_matrix[13]
+ * indicating the current state of the keyboard matrix.
+ *
+ * For EC_MKBP_EVENT_HOST_EVENT, return uint32_t host_event, the raw
+ * event state.
+ *
+ * For EC_MKBP_EVENT_BUTTON, returns uint32_t buttons, indicating the
+ * state of supported buttons.
+ *
+ * For EC_MKBP_EVENT_SWITCH, returns uint32_t switches, indicating the
+ * state of supported switches.
+ */
+ EC_MKBP_INFO_CURRENT = 2,
+};
+
/* Simulate key press */
#define EC_CMD_MKBP_SIMULATE_KEY 0x62
@@ -1984,6 +2035,12 @@ enum ec_mkbp_event {
/* New Sensor FIFO data. The event data is fifo_info structure. */
EC_MKBP_EVENT_SENSOR_FIFO = 2,
+ /* The state of the non-matrixed buttons have changed. */
+ EC_MKBP_EVENT_BUTTON = 3,
+
+ /* The state of the switches have changed. */
+ EC_MKBP_EVENT_SWITCH = 4,
+
/* Number of MKBP events */
EC_MKBP_EVENT_COUNT,
};
@@ -1993,6 +2050,9 @@ union ec_response_get_next_data {
/* Unaligned */
uint32_t host_event;
+
+ uint32_t buttons;
+ uint32_t switches;
} __packed;
struct ec_response_get_next_event {
@@ -2001,6 +2061,16 @@ struct ec_response_get_next_event {
union ec_response_get_next_data data;
} __packed;
+/* Bit indices for buttons and switches.*/
+/* Buttons */
+#define EC_MKBP_POWER_BUTTON 0
+#define EC_MKBP_VOL_UP 1
+#define EC_MKBP_VOL_DOWN 2
+
+/* Switches */
+#define EC_MKBP_LID_OPEN 0
+#define EC_MKBP_TABLET_MODE 1
+
/*****************************************************************************/
/* Temperature sensor commands */
@@ -2478,6 +2548,20 @@ struct ec_params_ext_power_current_limit {
uint32_t limit; /* in mA */
} __packed;
+/* Inform the EC when entering a sleep state */
+#define EC_CMD_HOST_SLEEP_EVENT 0xa9
+
+enum host_sleep_event {
+ HOST_SLEEP_EVENT_S3_SUSPEND = 1,
+ HOST_SLEEP_EVENT_S3_RESUME = 2,
+ HOST_SLEEP_EVENT_S0IX_SUSPEND = 3,
+ HOST_SLEEP_EVENT_S0IX_RESUME = 4
+};
+
+struct ec_params_host_sleep_event {
+ uint8_t sleep_event;
+} __packed;
+
/*****************************************************************************/
/* Smart battery pass-through */
diff --git a/include/linux/mfd/motorola-cpcap.h b/include/linux/mfd/motorola-cpcap.h
new file mode 100644
index 000000000000..b4031c2b2214
--- /dev/null
+++ b/include/linux/mfd/motorola-cpcap.h
@@ -0,0 +1,292 @@
+/*
+ * The register defines are based on earlier cpcap.h in Motorola Linux kernel
+ * tree.
+ *
+ * Copyright (C) 2007-2009 Motorola, Inc.
+ *
+ * Rewritten for the real register offsets instead of enumeration
+ * to make the defines usable with Linux kernel regmap support
+ *
+ * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define CPCAP_VENDOR_ST 0
+#define CPCAP_VENDOR_TI 1
+
+#define CPCAP_REVISION_MAJOR(r) (((r) >> 4) + 1)
+#define CPCAP_REVISION_MINOR(r) ((r) & 0xf)
+
+#define CPCAP_REVISION_1_0 0x08
+#define CPCAP_REVISION_1_1 0x09
+#define CPCAP_REVISION_2_0 0x10
+#define CPCAP_REVISION_2_1 0x11
+
+/* CPCAP registers */
+#define CPCAP_REG_INT1 0x0000 /* Interrupt 1 */
+#define CPCAP_REG_INT2 0x0004 /* Interrupt 2 */
+#define CPCAP_REG_INT3 0x0008 /* Interrupt 3 */
+#define CPCAP_REG_INT4 0x000c /* Interrupt 4 */
+#define CPCAP_REG_INTM1 0x0010 /* Interrupt Mask 1 */
+#define CPCAP_REG_INTM2 0x0014 /* Interrupt Mask 2 */
+#define CPCAP_REG_INTM3 0x0018 /* Interrupt Mask 3 */
+#define CPCAP_REG_INTM4 0x001c /* Interrupt Mask 4 */
+#define CPCAP_REG_INTS1 0x0020 /* Interrupt Sense 1 */
+#define CPCAP_REG_INTS2 0x0024 /* Interrupt Sense 2 */
+#define CPCAP_REG_INTS3 0x0028 /* Interrupt Sense 3 */
+#define CPCAP_REG_INTS4 0x002c /* Interrupt Sense 4 */
+#define CPCAP_REG_ASSIGN1 0x0030 /* Resource Assignment 1 */
+#define CPCAP_REG_ASSIGN2 0x0034 /* Resource Assignment 2 */
+#define CPCAP_REG_ASSIGN3 0x0038 /* Resource Assignment 3 */
+#define CPCAP_REG_ASSIGN4 0x003c /* Resource Assignment 4 */
+#define CPCAP_REG_ASSIGN5 0x0040 /* Resource Assignment 5 */
+#define CPCAP_REG_ASSIGN6 0x0044 /* Resource Assignment 6 */
+#define CPCAP_REG_VERSC1 0x0048 /* Version Control 1 */
+#define CPCAP_REG_VERSC2 0x004c /* Version Control 2 */
+
+#define CPCAP_REG_MI1 0x0200 /* Macro Interrupt 1 */
+#define CPCAP_REG_MIM1 0x0204 /* Macro Interrupt Mask 1 */
+#define CPCAP_REG_MI2 0x0208 /* Macro Interrupt 2 */
+#define CPCAP_REG_MIM2 0x020c /* Macro Interrupt Mask 2 */
+#define CPCAP_REG_UCC1 0x0210 /* UC Control 1 */
+#define CPCAP_REG_UCC2 0x0214 /* UC Control 2 */
+
+#define CPCAP_REG_PC1 0x021c /* Power Cut 1 */
+#define CPCAP_REG_PC2 0x0220 /* Power Cut 2 */
+#define CPCAP_REG_BPEOL 0x0224 /* BP and EOL */
+#define CPCAP_REG_PGC 0x0228 /* Power Gate and Control */
+#define CPCAP_REG_MT1 0x022c /* Memory Transfer 1 */
+#define CPCAP_REG_MT2 0x0230 /* Memory Transfer 2 */
+#define CPCAP_REG_MT3 0x0234 /* Memory Transfer 3 */
+#define CPCAP_REG_PF 0x0238 /* Print Format */
+
+#define CPCAP_REG_SCC 0x0400 /* System Clock Control */
+#define CPCAP_REG_SW1 0x0404 /* Stop Watch 1 */
+#define CPCAP_REG_SW2 0x0408 /* Stop Watch 2 */
+#define CPCAP_REG_UCTM 0x040c /* UC Turbo Mode */
+#define CPCAP_REG_TOD1 0x0410 /* Time of Day 1 */
+#define CPCAP_REG_TOD2 0x0414 /* Time of Day 2 */
+#define CPCAP_REG_TODA1 0x0418 /* Time of Day Alarm 1 */
+#define CPCAP_REG_TODA2 0x041c /* Time of Day Alarm 2 */
+#define CPCAP_REG_DAY 0x0420 /* Day */
+#define CPCAP_REG_DAYA 0x0424 /* Day Alarm */
+#define CPCAP_REG_VAL1 0x0428 /* Validity 1 */
+#define CPCAP_REG_VAL2 0x042c /* Validity 2 */
+
+#define CPCAP_REG_SDVSPLL 0x0600 /* Switcher DVS and PLL */
+#define CPCAP_REG_SI2CC1 0x0604 /* Switcher I2C Control 1 */
+#define CPCAP_REG_Si2CC2 0x0608 /* Switcher I2C Control 2 */
+#define CPCAP_REG_S1C1 0x060c /* Switcher 1 Control 1 */
+#define CPCAP_REG_S1C2 0x0610 /* Switcher 1 Control 2 */
+#define CPCAP_REG_S2C1 0x0614 /* Switcher 2 Control 1 */
+#define CPCAP_REG_S2C2 0x0618 /* Switcher 2 Control 2 */
+#define CPCAP_REG_S3C 0x061c /* Switcher 3 Control */
+#define CPCAP_REG_S4C1 0x0620 /* Switcher 4 Control 1 */
+#define CPCAP_REG_S4C2 0x0624 /* Switcher 4 Control 2 */
+#define CPCAP_REG_S5C 0x0628 /* Switcher 5 Control */
+#define CPCAP_REG_S6C 0x062c /* Switcher 6 Control */
+#define CPCAP_REG_VCAMC 0x0630 /* VCAM Control */
+#define CPCAP_REG_VCSIC 0x0634 /* VCSI Control */
+#define CPCAP_REG_VDACC 0x0638 /* VDAC Control */
+#define CPCAP_REG_VDIGC 0x063c /* VDIG Control */
+#define CPCAP_REG_VFUSEC 0x0640 /* VFUSE Control */
+#define CPCAP_REG_VHVIOC 0x0644 /* VHVIO Control */
+#define CPCAP_REG_VSDIOC 0x0648 /* VSDIO Control */
+#define CPCAP_REG_VPLLC 0x064c /* VPLL Control */
+#define CPCAP_REG_VRF1C 0x0650 /* VRF1 Control */
+#define CPCAP_REG_VRF2C 0x0654 /* VRF2 Control */
+#define CPCAP_REG_VRFREFC 0x0658 /* VRFREF Control */
+#define CPCAP_REG_VWLAN1C 0x065c /* VWLAN1 Control */
+#define CPCAP_REG_VWLAN2C 0x0660 /* VWLAN2 Control */
+#define CPCAP_REG_VSIMC 0x0664 /* VSIM Control */
+#define CPCAP_REG_VVIBC 0x0668 /* VVIB Control */
+#define CPCAP_REG_VUSBC 0x066c /* VUSB Control */
+#define CPCAP_REG_VUSBINT1C 0x0670 /* VUSBINT1 Control */
+#define CPCAP_REG_VUSBINT2C 0x0674 /* VUSBINT2 Control */
+#define CPCAP_REG_URT 0x0678 /* Useroff Regulator Trigger */
+#define CPCAP_REG_URM1 0x067c /* Useroff Regulator Mask 1 */
+#define CPCAP_REG_URM2 0x0680 /* Useroff Regulator Mask 2 */
+
+#define CPCAP_REG_VAUDIOC 0x0800 /* VAUDIO Control */
+#define CPCAP_REG_CC 0x0804 /* Codec Control */
+#define CPCAP_REG_CDI 0x0808 /* Codec Digital Interface */
+#define CPCAP_REG_SDAC 0x080c /* Stereo DAC */
+#define CPCAP_REG_SDACDI 0x0810 /* Stereo DAC Digital Interface */
+#define CPCAP_REG_TXI 0x0814 /* TX Inputs */
+#define CPCAP_REG_TXMP 0x0818 /* TX MIC PGA's */
+#define CPCAP_REG_RXOA 0x081c /* RX Output Amplifiers */
+#define CPCAP_REG_RXVC 0x0820 /* RX Volume Control */
+#define CPCAP_REG_RXCOA 0x0824 /* RX Codec to Output Amps */
+#define CPCAP_REG_RXSDOA 0x0828 /* RX Stereo DAC to Output Amps */
+#define CPCAP_REG_RXEPOA 0x082c /* RX External PGA to Output Amps */
+#define CPCAP_REG_RXLL 0x0830 /* RX Low Latency */
+#define CPCAP_REG_A2LA 0x0834 /* A2 Loudspeaker Amplifier */
+#define CPCAP_REG_MIPIS1 0x0838 /* MIPI Slimbus 1 */
+#define CPCAP_REG_MIPIS2 0x083c /* MIPI Slimbus 2 */
+#define CPCAP_REG_MIPIS3 0x0840 /* MIPI Slimbus 3. */
+#define CPCAP_REG_LVAB 0x0844 /* LMR Volume and A4 Balanced. */
+
+#define CPCAP_REG_CCC1 0x0a00 /* Coulomb Counter Control 1 */
+#define CPCAP_REG_CRM 0x0a04 /* Charger and Reverse Mode */
+#define CPCAP_REG_CCCC2 0x0a08 /* Coincell and Coulomb Ctr Ctrl 2 */
+#define CPCAP_REG_CCS1 0x0a0c /* Coulomb Counter Sample 1 */
+#define CPCAP_REG_CCS2 0x0a10 /* Coulomb Counter Sample 2 */
+#define CPCAP_REG_CCA1 0x0a14 /* Coulomb Counter Accumulator 1 */
+#define CPCAP_REG_CCA2 0x0a18 /* Coulomb Counter Accumulator 2 */
+#define CPCAP_REG_CCM 0x0a1c /* Coulomb Counter Mode */
+#define CPCAP_REG_CCO 0x0a20 /* Coulomb Counter Offset */
+#define CPCAP_REG_CCI 0x0a24 /* Coulomb Counter Integrator */
+
+#define CPCAP_REG_ADCC1 0x0c00 /* A/D Converter Configuration 1 */
+#define CPCAP_REG_ADCC2 0x0c04 /* A/D Converter Configuration 2 */
+#define CPCAP_REG_ADCD0 0x0c08 /* A/D Converter Data 0 */
+#define CPCAP_REG_ADCD1 0x0c0c /* A/D Converter Data 1 */
+#define CPCAP_REG_ADCD2 0x0c10 /* A/D Converter Data 2 */
+#define CPCAP_REG_ADCD3 0x0c14 /* A/D Converter Data 3 */
+#define CPCAP_REG_ADCD4 0x0c18 /* A/D Converter Data 4 */
+#define CPCAP_REG_ADCD5 0x0c1c /* A/D Converter Data 5 */
+#define CPCAP_REG_ADCD6 0x0c20 /* A/D Converter Data 6 */
+#define CPCAP_REG_ADCD7 0x0c24 /* A/D Converter Data 7 */
+#define CPCAP_REG_ADCAL1 0x0c28 /* A/D Converter Calibration 1 */
+#define CPCAP_REG_ADCAL2 0x0c2c /* A/D Converter Calibration 2 */
+
+#define CPCAP_REG_USBC1 0x0e00 /* USB Control 1 */
+#define CPCAP_REG_USBC2 0x0e04 /* USB Control 2 */
+#define CPCAP_REG_USBC3 0x0e08 /* USB Control 3 */
+#define CPCAP_REG_UVIDL 0x0e0c /* ULPI Vendor ID Low */
+#define CPCAP_REG_UVIDH 0x0e10 /* ULPI Vendor ID High */
+#define CPCAP_REG_UPIDL 0x0e14 /* ULPI Product ID Low */
+#define CPCAP_REG_UPIDH 0x0e18 /* ULPI Product ID High */
+#define CPCAP_REG_UFC1 0x0e1c /* ULPI Function Control 1 */
+#define CPCAP_REG_UFC2 0x0e20 /* ULPI Function Control 2 */
+#define CPCAP_REG_UFC3 0x0e24 /* ULPI Function Control 3 */
+#define CPCAP_REG_UIC1 0x0e28 /* ULPI Interface Control 1 */
+#define CPCAP_REG_UIC2 0x0e2c /* ULPI Interface Control 2 */
+#define CPCAP_REG_UIC3 0x0e30 /* ULPI Interface Control 3 */
+#define CPCAP_REG_USBOTG1 0x0e34 /* USB OTG Control 1 */
+#define CPCAP_REG_USBOTG2 0x0e38 /* USB OTG Control 2 */
+#define CPCAP_REG_USBOTG3 0x0e3c /* USB OTG Control 3 */
+#define CPCAP_REG_UIER1 0x0e40 /* USB Interrupt Enable Rising 1 */
+#define CPCAP_REG_UIER2 0x0e44 /* USB Interrupt Enable Rising 2 */
+#define CPCAP_REG_UIER3 0x0e48 /* USB Interrupt Enable Rising 3 */
+#define CPCAP_REG_UIEF1 0x0e4c /* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIEF2 0x0e50 /* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIEF3 0x0e54 /* USB Interrupt Enable Falling 1 */
+#define CPCAP_REG_UIS 0x0e58 /* USB Interrupt Status */
+#define CPCAP_REG_UIL 0x0e5c /* USB Interrupt Latch */
+#define CPCAP_REG_USBD 0x0e60 /* USB Debug */
+#define CPCAP_REG_SCR1 0x0e64 /* Scratch 1 */
+#define CPCAP_REG_SCR2 0x0e68 /* Scratch 2 */
+#define CPCAP_REG_SCR3 0x0e6c /* Scratch 3 */
+
+#define CPCAP_REG_VMC 0x0eac /* Video Mux Control */
+#define CPCAP_REG_OWDC 0x0eb0 /* One Wire Device Control */
+#define CPCAP_REG_GPIO0 0x0eb4 /* GPIO 0 Control */
+
+#define CPCAP_REG_GPIO1 0x0ebc /* GPIO 1 Control */
+
+#define CPCAP_REG_GPIO2 0x0ec4 /* GPIO 2 Control */
+
+#define CPCAP_REG_GPIO3 0x0ecc /* GPIO 3 Control */
+
+#define CPCAP_REG_GPIO4 0x0ed4 /* GPIO 4 Control */
+
+#define CPCAP_REG_GPIO5 0x0edc /* GPIO 5 Control */
+
+#define CPCAP_REG_GPIO6 0x0ee4 /* GPIO 6 Control */
+
+#define CPCAP_REG_MDLC 0x1000 /* Main Display Lighting Control */
+#define CPCAP_REG_KLC 0x1004 /* Keypad Lighting Control */
+#define CPCAP_REG_ADLC 0x1008 /* Aux Display Lighting Control */
+#define CPCAP_REG_REDC 0x100c /* Red Triode Control */
+#define CPCAP_REG_GREENC 0x1010 /* Green Triode Control */
+#define CPCAP_REG_BLUEC 0x1014 /* Blue Triode Control */
+#define CPCAP_REG_CFC 0x1018 /* Camera Flash Control */
+#define CPCAP_REG_ABC 0x101c /* Adaptive Boost Control */
+#define CPCAP_REG_BLEDC 0x1020 /* Bluetooth LED Control */
+#define CPCAP_REG_CLEDC 0x1024 /* Camera Privacy LED Control */
+
+#define CPCAP_REG_OW1C 0x1200 /* One Wire 1 Command */
+#define CPCAP_REG_OW1D 0x1204 /* One Wire 1 Data */
+#define CPCAP_REG_OW1I 0x1208 /* One Wire 1 Interrupt */
+#define CPCAP_REG_OW1IE 0x120c /* One Wire 1 Interrupt Enable */
+
+#define CPCAP_REG_OW1 0x1214 /* One Wire 1 Control */
+
+#define CPCAP_REG_OW2C 0x1220 /* One Wire 2 Command */
+#define CPCAP_REG_OW2D 0x1224 /* One Wire 2 Data */
+#define CPCAP_REG_OW2I 0x1228 /* One Wire 2 Interrupt */
+#define CPCAP_REG_OW2IE 0x122c /* One Wire 2 Interrupt Enable */
+
+#define CPCAP_REG_OW2 0x1234 /* One Wire 2 Control */
+
+#define CPCAP_REG_OW3C 0x1240 /* One Wire 3 Command */
+#define CPCAP_REG_OW3D 0x1244 /* One Wire 3 Data */
+#define CPCAP_REG_OW3I 0x1248 /* One Wire 3 Interrupt */
+#define CPCAP_REG_OW3IE 0x124c /* One Wire 3 Interrupt Enable */
+
+#define CPCAP_REG_OW3 0x1254 /* One Wire 3 Control */
+#define CPCAP_REG_GCAIC 0x1258 /* GCAI Clock Control */
+#define CPCAP_REG_GCAIM 0x125c /* GCAI GPIO Mode */
+#define CPCAP_REG_LGDIR 0x1260 /* LMR GCAI GPIO Direction */
+#define CPCAP_REG_LGPU 0x1264 /* LMR GCAI GPIO Pull-up */
+#define CPCAP_REG_LGPIN 0x1268 /* LMR GCAI GPIO Pin */
+#define CPCAP_REG_LGMASK 0x126c /* LMR GCAI GPIO Mask */
+#define CPCAP_REG_LDEB 0x1270 /* LMR Debounce Settings */
+#define CPCAP_REG_LGDET 0x1274 /* LMR GCAI Detach Detect */
+#define CPCAP_REG_LMISC 0x1278 /* LMR Misc Bits */
+#define CPCAP_REG_LMACE 0x127c /* LMR Mace IC Support */
+
+#define CPCAP_REG_TEST 0x7c00 /* Test */
+
+#define CPCAP_REG_ST_TEST1 0x7d08 /* ST Test1 */
+
+#define CPCAP_REG_ST_TEST2 0x7d18 /* ST Test2 */
+
+/*
+ * Helpers for child devices to check the revision and vendor.
+ *
+ * REVISIT: No documentation for the bits below, please update
+ * to use proper names for defines when available.
+ */
+
+static inline int cpcap_get_revision(struct device *dev,
+ struct regmap *regmap,
+ u16 *revision)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, CPCAP_REG_VERSC1, &val);
+ if (ret) {
+ dev_err(dev, "Could not read revision\n");
+
+ return ret;
+ }
+
+ *revision = ((val >> 3) & 0x7) | ((val << 3) & 0x38);
+
+ return 0;
+}
+
+static inline int cpcap_get_vendor(struct device *dev,
+ struct regmap *regmap,
+ u16 *vendor)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, CPCAP_REG_VERSC1, &val);
+ if (ret) {
+ dev_err(dev, "Could not read vendor\n");
+
+ return ret;
+ }
+
+ *vendor = (val >> 6) & 0x7;
+
+ return 0;
+}