From b9a72771779e960b3ddf14d372ed6484cba99cbc Mon Sep 17 00:00:00 2001 From: Lionel VITTE Date: Fri, 8 Nov 2019 16:52:41 +0100 Subject: [PATCH 12/31] ARM stm32mp1 r3 INPUT IRQ Mailbox --- drivers/input/misc/Kconfig | 11 ++ drivers/input/misc/Makefile | 2 + drivers/input/misc/stpmic1_onkey.c | 198 +++++++++++++++++++ drivers/input/touchscreen/edt-ft5x06.c | 24 ++- drivers/input/touchscreen/goodix.c | 24 +++ drivers/irqchip/irq-stm32-exti.c | 339 +++++++++++++++++++++++++++------ drivers/mailbox/mailbox-test.c | 12 +- drivers/mailbox/stm32-ipcc.c | 4 +- kernel/irq/internals.h | 4 - kernel/irq/manage.c | 75 ++------ 10 files changed, 563 insertions(+), 130 deletions(-) create mode 100644 drivers/input/misc/stpmic1_onkey.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2b..279fb02 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -851,4 +851,15 @@ config INPUT_SC27XX_VIBRA To compile this driver as a module, choose M here. The module will be called sc27xx_vibra. +config INPUT_STPMIC1_ONKEY + tristate "STPMIC1 PMIC Onkey support" + depends on MFD_STPMIC1 + help + Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey + can be used to wakeup from low power modes and force a shut-down on + long press. + + To compile this driver as a module, choose M here: the + module will be called stpmic1_onkey. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1..1b44202 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o @@ -81,3 +82,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o + diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c new file mode 100644 index 0000000..7b49c99 --- /dev/null +++ b/drivers/input/misc/stpmic1_onkey.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet for STMicroelectronics. + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct stpmic1_onkey - OnKey data + * @input_dev: pointer to input device + * @irq_falling: irq that we are hooked on to + * @irq_rising: irq that we are hooked on to + */ +struct stpmic1_onkey { + struct input_dev *input_dev; + int irq_falling; + int irq_rising; +}; + +static irqreturn_t onkey_falling_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 1); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t onkey_rising_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 0); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static int stpmic1_onkey_probe(struct platform_device *pdev) +{ + struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct stpmic1_onkey *onkey; + unsigned int val, reg = 0; + int error; + + onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) + return -ENOMEM; + + onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); + if (onkey->irq_falling < 0) { + dev_err(dev, "failed: request IRQ onkey-falling %d\n", + onkey->irq_falling); + return onkey->irq_falling; + } + + onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); + if (onkey->irq_rising < 0) { + dev_err(dev, "failed: request IRQ onkey-rising %d\n", + onkey->irq_rising); + return onkey->irq_rising; + } + + if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { + if (val > 0 && val <= 16) { + dev_dbg(dev, "power-off-time=%d seconds\n", val); + reg |= PONKEY_PWR_OFF; + reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); + } else { + dev_err(dev, "power-off-time-sec out of range\n"); + return -EINVAL; + } + } + + if (device_property_present(dev, "st,onkey-clear-cc-flag")) + reg |= PONKEY_CC_FLAG_CLEAR; + + error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, + PONKEY_TURNOFF_MASK, reg); + if (error) { + dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); + return error; + } + + if (device_property_present(dev, "st,onkey-pu-inactive")) { + error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, + PONKEY_PU_INACTIVE, + PONKEY_PU_INACTIVE); + if (error) { + dev_err(dev, "ONKEY Pads configuration failed: %d\n", + error); + return error; + } + } + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); + return -ENOMEM; + } + + input_dev->name = "pmic_onkey"; + input_dev->phys = "pmic_onkey/input0"; + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + onkey->input_dev = input_dev; + + /* interrupt is nested in a thread */ + error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, + onkey_falling_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, + onkey_rising_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, onkey); + device_init_wakeup(dev, true); + + return 0; +} + +static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + enable_irq_wake(onkey->irq_falling); + enable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static int __maybe_unused stpmic1_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(onkey->irq_falling); + disable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, + stpmic1_onkey_suspend, + stpmic1_onkey_resume); + +static const struct of_device_id of_stpmic1_onkey_match[] = { + { .compatible = "st,stpmic1-onkey" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); + +static struct platform_driver stpmic1_onkey_driver = { + .probe = stpmic1_onkey_probe, + .driver = { + .name = "stpmic1_onkey", + .of_match_table = of_match_ptr(of_stpmic1_onkey_match), + .pm = &stpmic1_onkey_pm, + }, +}; +module_platform_driver(stpmic1_onkey_driver); + +MODULE_DESCRIPTION("Onkey driver for STPMIC1"); +MODULE_AUTHOR("Pascal Paillet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1e18ca0..745da4c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -39,6 +39,8 @@ #include #include #include +#include + #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -968,6 +970,8 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, { const struct edt_i2c_chip_data *chip_data; struct edt_ft5x06_ts_data *tsdata; + struct mipi_dsi_device *panel; + struct device_node *np; struct input_dev *input; unsigned long irq_flags; int error; @@ -1033,7 +1037,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { - dev_err(&client->dev, "touchscreen probe failed\n"); + dev_dbg(&client->dev, "touchscreen probe failed\n"); return error; } @@ -1105,6 +1109,18 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1, tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1); + np = of_parse_phandle(client->dev.of_node, "panel", 0); + if (np) { + panel = of_find_mipi_dsi_device_by_node(np); + of_node_put(np); + if (!panel) + return -ENOENT; + + device_link_add(&client->dev, &panel->dev, DL_FLAG_STATELESS | + DL_FLAG_AUTOREMOVE_SUPPLIER); + put_device(&panel->dev); + } + return 0; } @@ -1152,11 +1168,16 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { .max_support_points = 2, }; +static const struct edt_i2c_chip_data edt_ft6336_data = { + .max_support_points = 2, +}; + static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, + { .name = "ft6336", .driver_data = (long)&edt_ft6336_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); @@ -1169,6 +1190,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, + { .compatible = "focaltech,ft6336", .data = &edt_ft6336_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b20ba65..c196423 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -25,8 +25,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -366,6 +368,13 @@ static void goodix_free_irq(struct goodix_ts_data *ts) static int goodix_request_irq(struct goodix_ts_data *ts) { + int gpio; + + gpio = desc_to_gpio(ts->gpiod_int); + + if (gpio_is_valid(gpio)) + ts->client->irq = gpio_to_irq(gpio); + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, NULL, goodix_ts_irq_handler, ts->irq_flags, ts->client->name, ts); @@ -774,6 +783,8 @@ static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; + struct mipi_dsi_device *panel; + struct device_node *np; int error; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -842,6 +853,17 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } + np = of_parse_phandle(client->dev.of_node, "panel", 0); + if (np) { + panel = of_find_mipi_dsi_device_by_node(np); + of_node_put(np); + if (!panel) + return -ENOENT; + device_link_add(&client->dev, &panel->dev, DL_FLAG_STATELESS | + DL_FLAG_AUTOREMOVE_SUPPLIER); + put_device(&panel->dev); + } + return 0; } @@ -896,6 +918,7 @@ static int __maybe_unused goodix_suspend(struct device *dev) * sooner, delay 58ms here. */ msleep(58); + return 0; } @@ -958,6 +981,7 @@ static const struct of_device_id goodix_of_match[] = { { .compatible = "goodix,gt9271" }, { .compatible = "goodix,gt928" }, { .compatible = "goodix,gt967" }, + { .compatible = "goodix,gt9147",}, { } }; MODULE_DEVICE_TABLE(of, goodix_of_match); diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 97b27f3..8e5a31b 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -6,20 +6,27 @@ */ #include +#include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #define IRQS_PER_BANK 32 +#define HWSPNLCK_TIMEOUT 1000 /* usec */ +#define HWSPNLCK_RETRY_DELAY 100 /* usec */ + struct stm32_exti_bank { u32 imr_ofst; u32 emr_ofst; @@ -58,6 +65,7 @@ struct stm32_exti_host_data { void __iomem *base; struct stm32_exti_chip_data *chips_data; const struct stm32_exti_drv_data *drv_data; + struct hwspinlock *hwlock; }; static struct stm32_exti_host_data *stm32_host_data; @@ -269,6 +277,42 @@ static int stm32_exti_set_type(struct irq_data *d, return 0; } +static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) +{ + int ret, timeout = 0; + + if (!chip_data->host_data->hwlock) + return 0; + + /* + * Use the x_raw API since we are under spin_lock protection. + * Do not use the x_timeout API because we are under irq_disable + * mode (see __setup_irq()) + */ + do { + ret = hwspin_trylock_raw(chip_data->host_data->hwlock); + if (!ret) + return 0; + + udelay(HWSPNLCK_RETRY_DELAY); + timeout += HWSPNLCK_RETRY_DELAY; + } while (timeout < HWSPNLCK_TIMEOUT); + + if (ret == -EBUSY) + ret = -ETIMEDOUT; + + if (ret) + pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); + + return ret; +} + +static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) +{ + if (chip_data->host_data->hwlock) + hwspin_unlock_raw(chip_data->host_data->hwlock); +} + static int stm32_irq_set_type(struct irq_data *d, unsigned int type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); @@ -279,21 +323,26 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) irq_gc_lock(gc); + err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) { - irq_gc_unlock(gc); - return err; - } + if (err) + goto unspinlock; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); +unspinlock: + stm32_exti_hwspin_unlock(chip_data); +unlock: irq_gc_unlock(gc); - return 0; + return err; } static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, @@ -460,20 +509,30 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) int err; raw_spin_lock(&chip_data->rlock); + + err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) { - raw_spin_unlock(&chip_data->rlock); - return err; - } + if (err) + goto unspinlock; writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + +unspinlock: + stm32_exti_hwspin_unlock(chip_data); +unlock: raw_spin_unlock(&chip_data->rlock); - return 0; + if (d->parent_data->chip) + irq_chip_set_type_parent(d, type); + + return err; } static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) @@ -490,6 +549,9 @@ static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) raw_spin_unlock(&chip_data->rlock); + if (d->parent_data->chip) + irq_chip_set_wake_parent(d, on); + return 0; } @@ -499,11 +561,16 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, if (d->parent_data->chip) return irq_chip_set_affinity_parent(d, dest, force); - return -EINVAL; + return IRQ_SET_MASK_OK_DONE; } -#ifdef CONFIG_PM -static int stm32_exti_h_suspend(void) +static void stm32_exti_h_ack(struct irq_data *d) +{ + if (d->parent_data->chip) + irq_chip_ack_parent(d); +} + +static int __maybe_unused stm32_exti_h_suspend(void) { struct stm32_exti_chip_data *chip_data; int i; @@ -518,7 +585,7 @@ static int stm32_exti_h_suspend(void) return 0; } -static void stm32_exti_h_resume(void) +static void __maybe_unused stm32_exti_h_resume(void) { struct stm32_exti_chip_data *chip_data; int i; @@ -532,21 +599,42 @@ static void stm32_exti_h_resume(void) } static struct syscore_ops stm32_exti_h_syscore_ops = { +#ifdef CONFIG_PM_SLEEP .suspend = stm32_exti_h_suspend, .resume = stm32_exti_h_resume, +#endif }; -static void stm32_exti_h_syscore_init(void) +static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data) { + stm32_host_data = host_data; register_syscore_ops(&stm32_exti_h_syscore_ops); } -#else -static inline void stm32_exti_h_syscore_init(void) {} -#endif + +static void stm32_exti_h_syscore_deinit(void) +{ + unregister_syscore_ops(&stm32_exti_h_syscore_ops); +} + +static int stm32_exti_request_resources(struct irq_data *d) +{ + struct irq_chip *chip_parent = d->parent_data->chip; + + if (chip_parent && chip_parent->irq_request_resources) + return chip_parent->irq_request_resources(d->parent_data); + return 0; +} + +static void stm32_exti_release_resources(struct irq_data *d) +{ + if (d->parent_data->chip && d->parent_data->chip->irq_release_resources) + d->parent_data->chip->irq_release_resources(d->parent_data); +} static struct irq_chip stm32_exti_h_chip = { .name = "stm32-exti-h", .irq_eoi = stm32_exti_h_eoi, + .irq_ack = stm32_exti_h_ack, .irq_mask = stm32_exti_h_mask, .irq_unmask = stm32_exti_h_unmask, .irq_retrigger = irq_chip_retrigger_hierarchy, @@ -554,6 +642,8 @@ static struct irq_chip stm32_exti_h_chip = { .irq_set_wake = stm32_exti_h_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND, .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32_exti_h_set_affinity : NULL, + .irq_request_resources = stm32_exti_request_resources, + .irq_release_resources = stm32_exti_release_resources, }; static int stm32_exti_h_domain_alloc(struct irq_domain *dm, @@ -574,15 +664,29 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm, irq_domain_set_hwirq_and_chip(dm, virq, hwirq, &stm32_exti_h_chip, chip_data); - p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); - if (p_irq >= 0) { + /* + * EXTI 55 to 60 are mapped to PWR interrupt controller. + * The hwirq translation is done diferently than for GIC. + */ + if (hwirq >= 55 && hwirq <= 60) { p_fwspec.fwnode = dm->parent->fwnode; - p_fwspec.param_count = 3; - p_fwspec.param[0] = GIC_SPI; - p_fwspec.param[1] = p_irq; - p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + p_fwspec.param_count = 2; + p_fwspec.param[0] = hwirq - 55; + p_fwspec.param[1] = fwspec->param[1]; return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } else { + p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); + if (p_irq >= 0) { + p_fwspec.fwnode = dm->parent->fwnode; + p_fwspec.param_count = 3; + p_fwspec.param[0] = GIC_SPI; + p_fwspec.param[1] = p_irq; + p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + + return irq_domain_alloc_irqs_parent(dm, virq, 1, + &p_fwspec); + } } return 0; @@ -631,7 +735,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, const struct stm32_exti_bank *stm32_bank; struct stm32_exti_chip_data *chip_data; void __iomem *base = h_data->base; - u32 irqs_mask; stm32_bank = h_data->drv_data->exti_banks[bank_idx]; chip_data = &h_data->chips_data[bank_idx]; @@ -640,10 +743,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, raw_spin_lock_init(&chip_data->rlock); - /* Determine number of irqs supported */ - writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); - irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); - /* * This IP has no reset, so after hot reboot we should * clear registers to avoid residue @@ -651,8 +750,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, writel_relaxed(0, base + stm32_bank->imr_ofst); writel_relaxed(0, base + stm32_bank->emr_ofst); - pr_info("%s: bank%d, External IRQs available:%#x\n", - node->full_name, bank_idx, irqs_mask); + pr_info("%pOF: bank%d\n", node, bank_idx); return chip_data; } @@ -730,55 +828,182 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, return ret; } +static int stm32_exti_h_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 2) + return -EINVAL; + + *out_hwirq = fwspec->param[0]; + *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; + } + + return -EINVAL; +} + static const struct irq_domain_ops stm32_exti_h_domain_ops = { .alloc = stm32_exti_h_domain_alloc, .free = irq_domain_free_irqs_common, + .translate = stm32_exti_h_translate, }; -static int -__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, - struct device_node *node, - struct device_node *parent) +static void stm32_exti_remove_irq(void *data) { + struct irq_domain *domain = data; + + irq_domain_remove(domain); +} + +static int stm32_exti_remove(struct platform_device *pdev) +{ + stm32_exti_h_syscore_deinit(); + return 0; +} + +static int stm32_exti_probe(struct platform_device *pdev) +{ + int ret, i; + u32 nirqs; + struct device *dev = &pdev->dev; + struct device_node *child, *np = dev->of_node; struct irq_domain *parent_domain, *domain; struct stm32_exti_host_data *host_data; - int ret, i; + const struct stm32_exti_drv_data *drv_data; + struct resource *res; - parent_domain = irq_find_host(parent); - if (!parent_domain) { - pr_err("interrupt-parent not found\n"); - return -EINVAL; + host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); + if (!host_data) + return -ENOMEM; + + /* check for optional hwspinlock which may be not available yet */ + ret = of_hwspin_lock_get_id(np, 0); + if (ret == -EPROBE_DEFER) + /* hwspinlock framework not yet ready */ + return ret; + + if (ret >= 0) { + host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!host_data->hwlock) { + dev_err(dev, "Failed to request hwspinlock\n"); + return -EINVAL; + } + } else if (ret != -ENOENT) { + /* note: ENOENT is a valid case (means 'no hwspinlock') */ + dev_err(dev, "Failed to get hwspinlock\n"); + return ret; } - host_data = stm32_exti_host_init(drv_data, node); - if (!host_data) + /* initialize host_data */ + drv_data = of_device_get_match_data(dev); + if (!drv_data) { + dev_err(dev, "no of match data\n"); + return -ENODEV; + } + host_data->drv_data = drv_data; + + host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, + sizeof(*host_data->chips_data), + GFP_KERNEL); + if (!host_data->chips_data) return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host_data->base = devm_ioremap_resource(dev, res); + if (IS_ERR(host_data->base)) { + dev_err(dev, "Unable to map registers\n"); + return PTR_ERR(host_data->base); + } + for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i, node); + stm32_exti_chip_init(host_data, i, np); + + parent_domain = irq_find_host(of_irq_find_parent(np)); + if (!parent_domain) { + dev_err(dev, "GIC interrupt-parent not found\n"); + return -EINVAL; + } domain = irq_domain_add_hierarchy(parent_domain, 0, drv_data->bank_nr * IRQS_PER_BANK, - node, &stm32_exti_h_domain_ops, + np, &stm32_exti_h_domain_ops, host_data); if (!domain) { - pr_err("%s: Could not register exti domain.\n", node->name); - ret = -ENOMEM; - goto out_unmap; + dev_err(dev, "Could not register exti domain\n"); + return -ENOMEM; } - stm32_exti_h_syscore_init(); + ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); + if (ret) + return ret; + + for_each_child_of_node(np, child) { + parent_domain = irq_find_host(of_irq_find_parent(child)); + if (!parent_domain) { + dev_err(dev, "child interrupt-parent not found\n"); + return -EINVAL; + } + + ret = of_property_read_u32(child, "st,irq-number", &nirqs); + if (ret || !nirqs) { + dev_err(dev, "Missing or bad irq-number property\n"); + return -EINVAL; + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, nirqs, + child, + &stm32_exti_h_domain_ops, + host_data); + if (!domain) { + dev_err(dev, "Could not register exti domain\n"); + return -ENOMEM; + } + + ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, + domain); + if (ret) + return ret; + } + + stm32_exti_h_syscore_init(host_data); return 0; +} -out_unmap: - iounmap(host_data->base); - kfree(host_data->chips_data); - kfree(host_data); - return ret; +/* platform driver only for MP1 */ +static const struct of_device_id stm32_exti_ids[] = { + { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_exti_ids); + +static struct platform_driver stm32_exti_driver = { + .probe = stm32_exti_probe, + .remove = stm32_exti_remove, + .driver = { + .name = "stm32_exti", + .of_match_table = stm32_exti_ids, + }, +}; + +static int __init stm32_exti_arch_init(void) +{ + return platform_driver_register(&stm32_exti_driver); +} + +static void __exit stm32_exti_arch_exit(void) +{ + return platform_driver_unregister(&stm32_exti_driver); } +arch_initcall(stm32_exti_arch_init); +module_exit(stm32_exti_arch_exit); + +/* no platform driver for F4 and H7 */ static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { @@ -794,11 +1019,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np, } IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); - -static int __init stm32mp1_exti_of_init(struct device_node *np, - struct device_node *parent) -{ - return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); -} - -IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 129b365..4e4ac4b 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -31,7 +31,6 @@ (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) static bool mbox_data_ready; -static struct dentry *root_debugfs_dir; struct mbox_test_device { struct device *dev; @@ -45,6 +44,7 @@ struct mbox_test_device { spinlock_t lock; wait_queue_head_t waitq; struct fasync_struct *async_queue; + struct dentry *root_debugfs_dir; }; static ssize_t mbox_test_signal_write(struct file *filp, @@ -262,16 +262,16 @@ static int mbox_test_add_debugfs(struct platform_device *pdev, if (!debugfs_initialized()) return 0; - root_debugfs_dir = debugfs_create_dir("mailbox", NULL); - if (!root_debugfs_dir) { + tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); + if (!tdev->root_debugfs_dir) { dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); return -EINVAL; } - debugfs_create_file("message", 0600, root_debugfs_dir, + debugfs_create_file("message", 0600, tdev->root_debugfs_dir, tdev, &mbox_test_message_ops); - debugfs_create_file("signal", 0200, root_debugfs_dir, + debugfs_create_file("signal", 0200, tdev->root_debugfs_dir, tdev, &mbox_test_signal_ops); return 0; @@ -418,7 +418,7 @@ static int mbox_test_remove(struct platform_device *pdev) { struct mbox_test_device *tdev = platform_get_drvdata(pdev); - debugfs_remove_recursive(root_debugfs_dir); + debugfs_remove_recursive(tdev->root_debugfs_dir); if (tdev->tx_channel) mbox_free_channel(tdev->tx_channel); diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index e313222..eaf4ea4 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -296,8 +296,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev) dev_err(dev, "Failed to set wake up irq\n"); goto err_init_wkp; } - } else { - device_init_wakeup(dev, false); + /* disable the wakeup source, let the user enable it or not */ + device_set_wakeup_enable(dev, false); } /* mailbox controller */ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index ea57f3d..c119aa1 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -95,10 +95,6 @@ static inline void irq_mark_irq(unsigned int irq) { } extern void irq_mark_irq(unsigned int irq); #endif -extern int __irq_get_irqchip_state(struct irq_data *data, - enum irqchip_irq_state which, - bool *state); - extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 23bcfa7..f8214bb 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -35,9 +35,8 @@ static int __init setup_forced_irqthreads(char *arg) early_param("threadirqs", setup_forced_irqthreads); #endif -static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) +static void __synchronize_hardirq(struct irq_desc *desc) { - struct irq_data *irqd = irq_desc_get_irq_data(desc); bool inprogress; do { @@ -53,20 +52,6 @@ static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) /* Ok, that indicated we're done: double-check carefully. */ raw_spin_lock_irqsave(&desc->lock, flags); inprogress = irqd_irq_inprogress(&desc->irq_data); - - /* - * If requested and supported, check at the chip whether it - * is in flight at the hardware level, i.e. already pending - * in a CPU and waiting for service and acknowledge. - */ - if (!inprogress && sync_chip) { - /* - * Ignore the return code. inprogress is only updated - * when the chip supports it. - */ - __irq_get_irqchip_state(irqd, IRQCHIP_STATE_ACTIVE, - &inprogress); - } raw_spin_unlock_irqrestore(&desc->lock, flags); /* Oops, that failed? */ @@ -89,18 +74,13 @@ static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) * Returns: false if a threaded handler is active. * * This function may be called - with care - from IRQ context. - * - * It does not check whether there is an interrupt in flight at the - * hardware level, but not serviced yet, as this might deadlock when - * called with interrupts disabled and the target CPU of the interrupt - * is the current CPU. */ bool synchronize_hardirq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (desc) { - __synchronize_hardirq(desc, false); + __synchronize_hardirq(desc); return !atomic_read(&desc->threads_active); } @@ -118,17 +98,13 @@ EXPORT_SYMBOL(synchronize_hardirq); * * Can only be called from preemptible code as it might sleep when * an interrupt thread is associated to @irq. - * - * It optionally makes sure (when the irq chip supports that method) - * that the interrupt is not pending in any CPU and waiting for - * service. */ void synchronize_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (desc) { - __synchronize_hardirq(desc, true); + __synchronize_hardirq(desc); /* * We made sure that no hardirq handler is * running. Now verify that no threaded handlers are @@ -1674,12 +1650,8 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id) unregister_handler_proc(irq, action); - /* - * Make sure it's not being used on another CPU and if the chip - * supports it also make sure that there is no (not yet serviced) - * interrupt in flight at the hardware level. - */ - __synchronize_hardirq(desc, true); + /* Make sure it's not being used on another CPU: */ + synchronize_hardirq(irq); #ifdef CONFIG_DEBUG_SHIRQ /* @@ -2212,28 +2184,6 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler, } EXPORT_SYMBOL_GPL(__request_percpu_irq); -int __irq_get_irqchip_state(struct irq_data *data, enum irqchip_irq_state which, - bool *state) -{ - struct irq_chip *chip; - int err = -EINVAL; - - do { - chip = irq_data_get_irq_chip(data); - if (chip->irq_get_irqchip_state) - break; -#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY - data = data->parent_data; -#else - data = NULL; -#endif - } while (data); - - if (data) - err = chip->irq_get_irqchip_state(data, which, state); - return err; -} - /** * irq_get_irqchip_state - returns the irqchip state of a interrupt. * @irq: Interrupt line that is forwarded to a VM @@ -2252,6 +2202,7 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, { struct irq_desc *desc; struct irq_data *data; + struct irq_chip *chip; unsigned long flags; int err = -EINVAL; @@ -2261,7 +2212,19 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, data = irq_desc_get_irq_data(desc); - err = __irq_get_irqchip_state(data, which, state); + do { + chip = irq_data_get_irq_chip(data); + if (chip->irq_get_irqchip_state) + break; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + data = data->parent_data; +#else + data = NULL; +#endif + } while (data); + + if (data) + err = chip->irq_get_irqchip_state(data, which, state); irq_put_desc_busunlock(desc, flags); return err; -- 2.7.4