diff options
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_prob |
