summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-23 19:18:01 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-23 19:18:01 +0300
commitdf9cdc1727ed9debfce59c5f600d794a63fcbfeb (patch)
tree03438886f80e90213a6be5b21ac0d601584213a5 /drivers
parentbc49a7831b1137ce1c2dda1c57e3631655f5d2ae (diff)
parente93c10211d03c35271896b03a40d3eca4a674770 (diff)
downloadlinux-df9cdc1727ed9debfce59c5f600d794a63fcbfeb.tar.xz
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 ...
Diffstat (limited to 'drivers')
-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
19 files changed, 961 insertions, 131 deletions
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);