diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-23 19:18:01 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-23 19:18:01 +0300 |
commit | df9cdc1727ed9debfce59c5f600d794a63fcbfeb (patch) | |
tree | 03438886f80e90213a6be5b21ac0d601584213a5 /drivers | |
parent | bc49a7831b1137ce1c2dda1c57e3631655f5d2ae (diff) | |
parent | e93c10211d03c35271896b03a40d3eca4a674770 (diff) | |
download | linux-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.c | 452 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/ab8500-core.c | 4 | ||||
-rw-r--r-- | drivers/mfd/ab8500-sysctrl.c | 14 | ||||
-rw-r--r-- | drivers/mfd/arizona-irq.c | 86 | ||||
-rw-r--r-- | drivers/mfd/arizona.h | 2 | ||||
-rw-r--r-- | drivers/mfd/axp20x.c | 78 | ||||
-rw-r--r-- | drivers/mfd/cros_ec.c | 53 | ||||
-rw-r--r-- | drivers/mfd/intel-lpss-pci.c | 17 | ||||
-rw-r--r-- | drivers/mfd/kempld-core.c | 40 | ||||
-rw-r--r-- | drivers/mfd/lpc_ich.c | 17 | ||||
-rw-r--r-- | drivers/mfd/max77686.c | 25 | ||||
-rw-r--r-- | drivers/mfd/motorola-cpcap.c | 259 | ||||
-rw-r--r-- | drivers/mfd/mt6397-core.c | 4 | ||||
-rw-r--r-- | drivers/mfd/rk808.c | 4 | ||||
-rw-r--r-- | drivers/mfd/sun6i-prcm.c | 13 | ||||
-rw-r--r-- | drivers/mfd/tps65912-i2c.c | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_proto.c | 5 |
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); |