From cdd7950e7aa4a4d0d8ba71e3967aae6d25d09b03 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 20 Jan 2017 11:14:15 +0100 Subject: input: cros_ec_keyb: Add non-matrix buttons and switches On some newer boards using mkbp we're hooking up non-matrix buttons and switches to the EC but NOT to the main application processor. Let's add kernel support to handle this. Rather than creating a whole new input driver, we'll continue to use cros_ec_keyb and just report the new keys. Signed-off-by: Douglas Anderson Signed-off-by: Enric Balletbo i Serra Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/keyboard/cros_ec_keyb.c | 447 ++++++++++++++++++++++++++++++---- 1 file changed, 402 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 25943e9bc8bf..ad74ebce0cdd 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -34,6 +34,8 @@ #include #include +#include + /* * @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,59 @@ 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, + }, +}; + /* * Returns true when there is at least one combination of pressed keys that * results in ghosting. @@ -149,20 +199,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 +233,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_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; - if (ckdev->ec->event_data.event_type != EC_MKBP_EVENT_KEY_MATRIX) + 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 +308,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 +542,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 +589,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 +648,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, }, }; -- cgit v1.2.3 From 6ccc3a33810e8ec09936fa990c13370d9f61606f Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 27 Jan 2017 11:52:35 +0100 Subject: input: cros_ec_keyb: Add Tablet Mode switch Add switch to report tablet mode. Signed-off-by: Gwendal Grignou Signed-off-by: Enric Balletbo Serra Signed-off-by: Lee Jones --- drivers/input/keyboard/cros_ec_keyb.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index ad74ebce0cdd..604c7ade8df2 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -111,6 +111,11 @@ static const struct cros_ec_bs_map cros_ec_keyb_bs[] = { .bit = EC_MKBP_LID_OPEN, .inverted = true, }, + { + .ev_type = EV_SW, + .code = SW_TABLET_MODE, + .bit = EC_MKBP_TABLET_MODE, + }, }; /* -- cgit v1.2.3 From 003db34ef33eb2d8848e0962bd72ee42d7344271 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 22 Nov 2016 16:10:26 +0000 Subject: mfd: arizona: Use arizona_map_irq instead of hard coding it We have arizona_map_irq we might as well use it rather than hard coding it in several places. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-irq.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 2e01975f042d..724fa5404306 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -204,7 +204,7 @@ 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; @@ -368,9 +368,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 +378,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,7 +392,7 @@ 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: @@ -410,9 +408,9 @@ err: int arizona_irq_exit(struct arizona *arizona) { 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); + arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona); + arizona_free_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), -- cgit v1.2.3 From 1a86dcb3f1405a0152b9df7cebb75b839386c997 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 22 Nov 2016 16:10:27 +0000 Subject: mfd: arizona: Add defines for IRQs on the main Arizona IRQ domain Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-irq.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 724fa5404306..88729cf6cb32 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; @@ -319,7 +322,8 @@ int arizona_irq_init(struct arizona *arizona) if (aod) { ret = regmap_add_irq_chip(arizona->regmap, - irq_create_mapping(arizona->virq, 0), + irq_create_mapping(arizona->virq, + ARIZONA_AOD_IRQ_INDEX), IRQF_ONESHOT, 0, aod, &arizona->aod_irq_chip); if (ret != 0) { @@ -330,7 +334,8 @@ int arizona_irq_init(struct arizona *arizona) } ret = regmap_add_irq_chip(arizona->regmap, - irq_create_mapping(arizona->virq, 1), + irq_create_mapping(arizona->virq, + ARIZONA_MAIN_IRQ_INDEX), IRQF_ONESHOT, 0, irq, &arizona->irq_chip); if (ret != 0) { @@ -396,10 +401,12 @@ err_ctrlif: 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_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: return ret; @@ -411,9 +418,11 @@ int arizona_irq_exit(struct arizona *arizona) arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona); arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona); - 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); - 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); free_irq(arizona->irq, arizona); -- cgit v1.2.3 From 3dfaff274d558a4107393831afa0cf0989bc4ff1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 22 Nov 2016 16:10:28 +0000 Subject: mfd: arizona: Correctly clean up after IRQs Currently we leak a lot of things when tearing down the IRQs this patch fixes this cleaning up both the IRQ mappings and the IRQ domain itself. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-irq.c | 59 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 88729cf6cb32..09cf3699e354 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -210,6 +210,7 @@ int arizona_irq_init(struct arizona *arizona) int ret; const struct regmap_irq_chip *aod, *irq; struct irq_data *irq_data; + unsigned int virq; arizona->ctrlif_error = true; @@ -321,26 +322,34 @@ int arizona_irq_init(struct arizona *arizona) } if (aod) { - ret = regmap_add_irq_chip(arizona->regmap, - irq_create_mapping(arizona->virq, - ARIZONA_AOD_IRQ_INDEX), - 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, - ARIZONA_MAIN_IRQ_INDEX), - 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 */ @@ -404,26 +413,40 @@ err_main_irq: 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, 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) arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona); arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona); - regmap_del_irq_chip(irq_find_mapping(arizona->virq, - ARIZONA_MAIN_IRQ_INDEX), - arizona->irq_chip); - regmap_del_irq_chip(irq_find_mapping(arizona->virq, - ARIZONA_AOD_IRQ_INDEX), - arizona->aod_irq_chip); + 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; -- cgit v1.2.3 From ea1628e856b4c383609127b03767e8dfcbb5f94f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 25 Nov 2016 20:34:35 +0800 Subject: mfd: sun6i-prcm: Add codec analog controls sub-device for Allwinner A23 The PRCM block on the A23 contains a message box like interface to the registers for the analog path controls of the internal codec. Add a sub-device for it. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Lee Jones --- drivers/mfd/sun6i-prcm.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') 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 #include +#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 = { -- cgit v1.2.3 From b3b66158513edc4b8613ad83600809847739a51b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 28 Nov 2016 15:45:07 +0000 Subject: mfd: arizona: Remove totally unused forward declaration This declaration has never been used and is likely some left over from early prototypes of the code, just remove it. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') 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 #include -struct wm_arizona; - extern const struct regmap_config wm5102_i2c_regmap; extern const struct regmap_config wm5102_spi_regmap; -- cgit v1.2.3 From 0a5454c901aea0fef99f5ef7910c69c501817ae1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Dec 2016 14:52:05 +0100 Subject: mfd: axp20x: Use IRQF_TRIGGER_LOW on the axp288 The interrupt line of the entire family of axp2xx pmics is active-low, for devicetree enumerated irqs, this is dealt with in the devicetree. ACPI irq resources have a flag field for this too, I tried using this on my CUBE iwork8 Air tablet, but it does not contain the right data. The dstd shows the irq listed as either ActiveLow or ActiveHigh, depending on the OSID variable, which seems to be set by the "OS IMAGE ID" in the BIOS/EFI setup screen. Since the acpi-resource info is no good, simply pass in IRQF_TRIGGER_LOW on the axp288. Together with the other axp288 fixes in this series, this fixes the axp288 irq contineously triggering. Signed-off-by: Hans de Goede Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index ed918de84238..f124bc36f65d 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -802,6 +802,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); @@ -831,9 +832,8 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) int ret; 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; -- cgit v1.2.3 From 8b44e6782e426ccc8b025119171f7332fd119d5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Dec 2016 14:52:06 +0100 Subject: mfd: axp20x: Add missing axp288 irqs The axp288 has the following irqs 2 times: VBUS_FALL, VBUS_RISE, VBUS_OV. On boot / reset the enable flags for both the normal and alt version of these irqs is set. Since we were only listing the normal version in the axp288 regmap_irq struct, we were never disabling the alt versions of these irqs. Add the alt versions to the axp288 regmap_irq struct, so that these get properly disabled. Together with the other axp288 fixes in this series, this fixes the axp288 irq contineously triggering. Signed-off-by: Hans de Goede Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index f124bc36f65d..89b2ee5a5346 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -407,6 +407,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), -- cgit v1.2.3 From 1af468ebe45591651ec3bafc2e9ddc6fdef70ae0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Dec 2016 14:52:07 +0100 Subject: mfd: axp20x: Fix axp288 PEK_DBR and PEK_DBF irqs being swapped The R in PEK_DBR stands for rising, so it should be mapped to AXP288_IRQ_POKP where the last P stands for positive edge. Likewise PEK_DBF should be mapped to the falling edge, aka the _N_egative edge, so it should be mapped to AXP288_IRQ_POKN. This fixes the inverted powerbutton status reporting by the axp20x-pek driver. Signed-off-by: Hans de Goede Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 89b2ee5a5346..26ea174ba7cc 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -207,14 +207,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, }, }; -- cgit v1.2.3 From cd53216625a0fa1db130f8d564f47ba99c0947e3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Dec 2016 21:09:06 +0100 Subject: mfd: axp20x: Fix axp288 volatile ranges The axp288 pmic has a lot more volatile registers then we were listing in axp288_volatile_ranges, fix this. Signed-off-by: Hans de Goede Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 26ea174ba7cc..ed00ba0d64d2 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -118,7 +118,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 = { -- cgit v1.2.3 From 4c6505613385accf67413ded85e688c5d57037f5 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Fri, 9 Dec 2016 12:04:14 +0100 Subject: mfd: axp20x: Add separate MFD cell for AXP223 The AXP223 shares most of its logic with the AXP221 but has some differences for the VBUS power supply driver. Thus, to probe the driver with the correct compatible, the AXP221 and the AXP223 now have separate MFD cells. AXP221 MFD cells are renamed from axp22x_cells to axp221_cells to avoid confusion. Signed-off-by: Quentin Schulz Acked-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index ed00ba0d64d2..20b95af9a217 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -599,7 +599,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), @@ -608,7 +623,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, }, @@ -801,9 +816,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; -- cgit v1.2.3 From 149da46a230947731d379c89eb9aef21d41f9115 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 13 Dec 2016 15:33:31 +0100 Subject: mfd: Kconfig: MFD_SUN4I_GPADC depends on !TOUCHSCREN_SUN4I_GPADC MFD_SUN4I_GPADC and TOUCHSCREEN_SUN4I are incompatible (both are drivers for Allwinner SoCs' ADC). This makes sure TOUCHSCREEN_SUN4I isn't enabled while MFD_SUN4I_GPADC is enabled. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d0c14b88b24e..dbebe20c6d6d 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 -- cgit v1.2.3 From a9eb186e13144782232cc6fa731441be54baf505 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Fri, 16 Dec 2016 18:57:36 +0100 Subject: mfd: cros_ec: Prevent data transfer while device is suspended The cros_ec driver is still active while the device is suspended. Besides that, it also tries to transfer data even after the I2C host had been suspended. This patch uses a simple flag to prevent this. Signed-off-by: Joseph Lo Signed-off-by: Thierry Escande Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 2 ++ drivers/platform/chrome/cros_ec_proto.c | 5 +++++ 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index abd83424b498..ad48633c9a47 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -165,6 +165,7 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) disable_irq(ec_dev->irq); ec_dev->was_wake_device = ec_dev->wake_enabled; + ec_dev->suspended = true; return 0; } @@ -179,6 +180,7 @@ static void cros_ec_drain_events(struct cros_ec_device *ec_dev) int cros_ec_resume(struct cros_ec_device *ec_dev) { + ec_dev->suspended = false; enable_irq(ec_dev->irq); /* 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); -- cgit v1.2.3 From f00c06fd98576face871e62bb3aa045c5f647661 Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Fri, 16 Dec 2016 18:57:37 +0100 Subject: mfd: cros_ec: Send suspend state notification to EC Notify EC when going to or returning from suspend so that proper actions related to wake events can be taken. Signed-off-by: Shawn Nematbakhsh Signed-off-by: Thierry Escande Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index ad48633c9a47..b8a50808bedb 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #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,6 +187,16 @@ 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 = 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); @@ -180,9 +218,20 @@ 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 = 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, -- cgit v1.2.3 From 696f0b3f4ae904d45a24c7bbc728cbbddd631af0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 5 Jan 2017 12:01:03 +0800 Subject: mfd: axp20x: Fix AXP806 access errors on cold boot 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. Only when the these device addressing bits (in the upper 4 bits of the registers) match, will the device respond to operations on its other registers. The AXP806_REG_ADDR_EXT was previously configured by Allwinner's bootloader. Work on U-boot SPL support now allows us to switch to mainline U-boot, which doesn't do this for us. There might be other bare minimum bootloaders out there which don't to this either. It's best to handle this in the kernel. This patch sets AXP806_REG_ADDR_EXT to 0x10, which is what we know to be the proper value for a standard AXP806 in slave mode. Afterwards it will reinitialize the regmap cache, to purge any invalid stale values. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 20b95af9a217..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", @@ -861,6 +863,30 @@ 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 | axp20x->irq_flags, -1, axp20x->regmap_irq_chip, &axp20x->regmap_irqc); -- cgit v1.2.3 From f80e78aa11ad754de20104233af1ce4cea8f16a5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Jan 2017 14:16:09 +0200 Subject: mfd: intel-lpss: Add Intel Gemini Lake PCI IDs Intel Gemini Lake is essentially Broxton with different PCI IDs. Add these new PCI IDs to the list of supported devices. Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-pci.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') 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 }, -- cgit v1.2.3 From 7e9c40c63933a643908d686bd89dfc2315e8c70a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Jan 2017 10:53:55 +0100 Subject: mfd: ab8500-sysctrl: Handle probe deferral In the current boot, clients making use of the AB8500 sysctrl may be probed before the ab8500-sysctrl driver. This gives them -EINVAL, but should rather give -EPROBE_DEFER. Before this, the abx500 clock driver didn't probe properly, and as a result the codec driver in turn using the clocks did not probe properly. After this patch, everything probes properly. Also add OF compatible-string probing. This driver is all device tree, so let's just make a drive-by-fix of that as well. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/ab8500-sysctrl.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') 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, -- cgit v1.2.3 From 17ee971f03ad2c0786842d3a6b551e842459e2ac Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 13 Jan 2017 10:34:05 -0300 Subject: mfd: max77686: Don't attempt to get i2c_device_id .data The driver is only used in platforms that have DT support so always the I2C device .data will be get from the matched OF node and never will be from the I2C device ID table. Signed-off-by: Javier Martinez Canillas Acked-by: Laxman Dewangan Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 7b68ed72e9cb..ddae3bf3e46c 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -188,14 +188,11 @@ 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; + match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node); + if (!match) + return -EINVAL; + + max77686->type = (unsigned long)match->data; i2c_set_clientdata(i2c, max77686); max77686->dev = &i2c->dev; -- cgit v1.2.3 From 68e61f75aba91077409a86740c0d5882145968e8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 13 Jan 2017 10:34:06 -0300 Subject: mfd: max77686: Use of_device_get_match_data() helper Use the generic helper to get the matched of_device_id .data, instead of open coding it. The driver was checking if matching the OF node with the driver's OF table was failing, but this doesn't make too much sense since this can't happen in practice. The fact the probe function was called, means OF registered a device with a valid compatible string so a of_device_get_match_data() call will always succeed. So just remove this unneeded check. Signed-off-by: Javier Martinez Canillas Acked-by: Laxman Dewangan Reviewed-by: Chanwoo Choi Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index ddae3bf3e46c..33dd09493605 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -34,6 +34,7 @@ #include #include #include +#include static const struct mfd_cell max77686_devs[] = { { .name = "max77686-pmic", }, @@ -175,7 +176,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max77686_dev *max77686 = NULL; - const struct of_device_id *match; unsigned int data; int ret = 0; const struct regmap_config *config; @@ -188,13 +188,8 @@ static int max77686_i2c_probe(struct i2c_client *i2c, if (!max77686) return -ENOMEM; - match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node); - if (!match) - return -EINVAL; - - max77686->type = (unsigned long)match->data; - i2c_set_clientdata(i2c, max77686); + max77686->type = (unsigned long)of_device_get_match_data(&i2c->dev); max77686->dev = &i2c->dev; max77686->i2c = i2c; -- cgit v1.2.3 From be1bb2355bf3a500d9eb4288f5a99225126b7453 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 13 Jan 2017 10:34:07 -0300 Subject: mfd: max77686: Use the struct i2c_driver .probe_new instead of .probe If a driver is only used in DT platforms, there's no need to get the i2c_device_id as an argument of the probe function. Since this data can be get from the matching of_device_id. There's a temporary .probe_new field in struct i2c_driver that can be used as probe callback for the case when i2c_device_id won't be used. Signed-off-by: Javier Martinez Canillas Acked-by: Laxman Dewangan Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 33dd09493605..896c1bf85acc 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -172,8 +172,7 @@ 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; unsigned int data; @@ -294,7 +293,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, + .probe_new = max77686_i2c_probe, .id_table = max77686_i2c_id, }; -- cgit v1.2.3 From 644b2ee06d9af0846caa70d13056d50917b98c67 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 13 Jan 2017 10:34:08 -0300 Subject: mfd: max77686: Remove I2C device ID table The driver is only used in DT platforms so there's no need to have an i2c_device_id table. Signed-off-by: Javier Martinez Canillas Acked-by: Laxman Dewangan Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 896c1bf85acc..b0e8e13c0049 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -241,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) { @@ -294,7 +287,6 @@ static struct i2c_driver max77686_i2c_driver = { .of_match_table = of_match_ptr(max77686_pmic_dt_match), }, .probe_new = max77686_i2c_probe, - .id_table = max77686_i2c_id, }; module_i2c_driver(max77686_i2c_driver); -- cgit v1.2.3 From 9c576bd35ef4086f25c04ec662019c33494ee2fb Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Fri, 13 Jan 2017 16:04:32 +0100 Subject: mfd: cros_ec: Send correct suspend/resume event to EC pm_suspend_via_firmware() will return false for platforms with ACPI disabled and ACPI is a prerequisite for S0ix support. With this patch, sleep state event sent to EC is forced to S3 if ACPI is disabled. Signed-off-by: Shawn Nematbakhsh Signed-off-by: Thierry Escande Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index b8a50808bedb..9b66a98ba4bf 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -190,8 +190,9 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) int ret; u8 sleep_event; - sleep_event = pm_suspend_via_firmware() ? HOST_SLEEP_EVENT_S3_RESUME : - HOST_SLEEP_EVENT_S0IX_RESUME; + 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) @@ -224,8 +225,9 @@ int cros_ec_resume(struct cros_ec_device *ec_dev) ec_dev->suspended = false; enable_irq(ec_dev->irq); - sleep_event = pm_suspend_via_firmware() ? HOST_SLEEP_EVENT_S3_RESUME : - HOST_SLEEP_EVENT_S0IX_RESUME; + 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) -- cgit v1.2.3 From 91549e87239f745de68fcda1af978c1f0aa17fcb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Jan 2017 16:10:20 +0100 Subject: mfd: ab8500-core: Rename clock device and compatible We didn't have proper device tree bindings for this clock, I standardized it to use the exact chipname so let's rename it "ab8500-clk" and rectify the device tree compatible string to "stericsson,ab8500-clk". Cc: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/ab8500-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') 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", -- cgit v1.2.3 From f8b0380544ec8b4fa21743af9319258abce5e093 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 16 Jan 2017 11:41:58 -0300 Subject: mfd: tps65912: Export OF device ID table as module aliases The I2C core always reports a MODALIAS of the form i2c: even if the device was registered via OF, this means that exporting the OF device ID table device aliases in the module is not needed. But in order to change how the core reports modaliases to user-space, it's better to export it. Before this patch: $ modinfo drivers/mfd/tps65912-i2c.ko | grep alias alias: i2c:tps65912 After this patch: $ modinfo drivers/mfd/tps65912-i2c.ko | grep alias alias: of:N*T*Cti,tps65912C* alias: of:N*T*Cti,tps65912 alias: i2c:tps65912 Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/tps65912-i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') 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) -- cgit v1.2.3 From 1cb8af8d631f66dde37a918bdd08a924ba3c7c03 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 23 Jan 2017 11:54:45 +0800 Subject: mfd: mt6397: Add MT6323 LED support into MT6397 driver Add compatible string as "mt6323-led" that will make the OF core spawn child devices for the LED subnode of that MT6323 MFD device. Signed-off-by: Sean Wang Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') 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[] = { -- cgit v1.2.3 From 56e1d40d3beab2f247d48574bf51fc5daeebc285 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 5 Jan 2017 16:44:39 -0800 Subject: mfd: cpcap: Add minimal support Many Motorola phones like droid 4 are using a custom PMIC called CPCAP or 6556002. We can support it's core features quite easily with regmap_spi and regmap_irq. The children of cpcap, such as regulators, ADC and USB, can be just regular device drivers and defined in the dts file. They get probed as we call of_platform_populate() at the end of our probe, and then the children can just call dev_get_regmap(dev.parent, NULL) to get the regmap. Cc: devicetree@vger.kernel.org Cc: Marcel Partap Cc: Mark Rutland Cc: Michael Scott Acked-by: Rob Herring Signed-off-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 11 ++ drivers/mfd/Makefile | 1 + drivers/mfd/motorola-cpcap.c | 259 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 drivers/mfd/motorola-cpcap.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index dbebe20c6d6d..496247a7f893 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -715,6 +715,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/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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 24e34b9d75a902f1371f810c164b494336bafeae Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 25 Jan 2017 00:55:24 +0530 Subject: mfd: constify regmap_irq_chip structures Declare regmap_irq_chip structures as const as they are only stored in the regmap_irq_chip field of a rk808 structure. This field is of type const, so regmap_irq_chip structures having this property can be made const too. Done using Coccinelle: @r disable optional_qualifier@ identifier x; position p; @@ static struct regmap_irq_chip x@p={...}; @ok@ struct rk808 a; identifier r.x; position p; @@ a.regmap_irq_chip=&x@p; @bad@ position p != {r.p,ok.p}; identifier r.x; @@ x@p @depends on !bad disable optional_qualifier@ identifier r.x; @@ +const struct regmap_irq_chip x; File size before: text data bss dec hex filename 5033 584 16 5633 1601 drivers/mfd/rk808.o File size after: text data bss dec hex filename 5225 392 16 5633 1601 drivers/mfd/rk808.o Signed-off-by: Bhumika Goyal Signed-off-by: Lee Jones --- drivers/mfd/rk808.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') 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), -- cgit v1.2.3 From e33ad65a58080072b0dfba50d34dfcce959dc944 Mon Sep 17 00:00:00 2001 From: Michael Brunner Date: Thu, 26 Jan 2017 17:45:46 +0100 Subject: mfd: Add support for several boards to Kontron PLD driver This patch adds the DMI system ID of the Kontron COMe-bBD#, COMe-bKL6, COMe-cKL6, COMe-bSL6 and COMe-cAL6 boards to the Kontron PLD driver. The list of supported products in the module description is also updated. Signed-off-by: Michael Brunner Acked-by: Christian Rauch Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 5 +++++ drivers/mfd/kempld-core.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 496247a7f893..55ecdfb74d31 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -507,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 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"), @@ -511,6 +519,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 = { @@ -599,6 +631,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 = { -- cgit v1.2.3 From 788fd8221d368b47dafd009bd4803bf69dca0307 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 28 Jan 2017 16:27:34 +0200 Subject: mfd: lpc_ich: Remove useless comments in core part First of all, remove stalled references to datasheets. If someone knows the document numbers, it would be added later. Second, remove FSF snail address since it's subject to change. Actual information can be found on FSF site on the internet. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index be42957a78e1..fd6e8006c6b6 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 -- cgit v1.2.3 From e93c10211d03c35271896b03a40d3eca4a674770 Mon Sep 17 00:00:00 2001 From: Tan Jui Nee Date: Sat, 28 Jan 2017 16:27:33 +0200 Subject: mfd: lpc_ich: Enable watchdog on Intel Apollo Lake PCH Assign iTCO_version which effectively enables watchdog device on Intel Apollo Lake PCH. Signed-off-by: Tan Jui Nee Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index fd6e8006c6b6..d98a5d974092 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -551,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, }, }; -- cgit v1.2.3