// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2017 - All Rights Reserved * Author: Pascal Paillet for STMicroelectronics. */ #include #include #include #include #include #include #include #include #include #define NB_WAKEUPPINS 6 #define STM32_SVC_PWR 0x82001001 #define STM32_WRITE 0x1 #define STM32_SET_BITS 0x2 #define STM32_CLEAR_BITS 0x3 #define PWR_WKUP_OFFSET 0x20 // PWR Registers #define WKUPCR 0x0 #define WKUPFR 0x4 #define MPUWKUPENR 0x8 #define WKUP_FLAGS_MASK GENMASK(5, 0) // WKUPCR bits definition #define WKUP_EDGE_SHIFT 8 #define WKUP_PULL_SHIFT 16 #define WKUP_PULL_MASK GENMASK(1, 0) enum wkup_pull_setting { WKUP_NO_PULL = 0, WKUP_PULL_UP, WKUP_PULL_DOWN, WKUP_PULL_RESERVED }; #define SMC(class, op, offset, val) do { \ struct arm_smccc_res res; \ arm_smccc_smc(class, op, PWR_WKUP_OFFSET + (offset), val, \ 0, 0, 0, 0, &res); \ } while (0) \ struct stm32_pwr_data { struct device *dev; void __iomem *base; /* IO Memory base address */ struct irq_domain *domain; /* Domain for this controller */ int irq; /* Parent interrupt */ u32 masked; /* IRQ is masked */ u32 wake; /* IRQ is wake on */ u32 pending; /* IRQ has been received while wake on*/ struct gpio_desc *gpio[NB_WAKEUPPINS]; }; static void stm32_pwr_irq_ack(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, BIT(d->hwirq)); } static void stm32_pwr_irq_set_enable(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); if (!(priv->masked & BIT(d->hwirq)) || (priv->wake & BIT(d->hwirq))) SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq)); else SMC(STM32_SVC_PWR, STM32_CLEAR_BITS, MPUWKUPENR, BIT(d->hwirq)); } static void stm32_pwr_irq_mask(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); priv->masked |= BIT(d->hwirq); stm32_pwr_irq_set_enable(d); } static void stm32_pwr_irq_unmask(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); priv->masked &= ~BIT(d->hwirq); stm32_pwr_irq_set_enable(d); } static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on) { struct stm32_pwr_data *priv = d->domain->host_data; struct irq_data *parent = irq_get_irq_data(priv->irq); dev_dbg(priv->dev, "irq:%lu on:%d\n", d->hwirq, on); if (on) { priv->wake |= BIT(d->hwirq); } else { priv->wake &= ~BIT(d->hwirq); priv->pending &= ~BIT(d->hwirq); } stm32_pwr_irq_set_enable(d); if (parent->chip && parent->chip->irq_set_wake) return parent->chip->irq_set_wake(parent, on); return 0; } static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct stm32_pwr_data *priv = d->domain->host_data; int pin_id = d->hwirq; u32 wkupcr; int en; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); en = readl_relaxed(priv->base + MPUWKUPENR) & BIT(pin_id); /* reference manual request to disable the wakeup pin while * changing the edge detection setting */ if (en) stm32_pwr_irq_mask(d); wkupcr = readl_relaxed(priv->base + WKUPCR); switch (flow_type & IRQ_TYPE_SENSE_MASK) { case IRQF_TRIGGER_FALLING: wkupcr |= (1 << (WKUP_EDGE_SHIFT + pin_id)); break; case IRQF_TRIGGER_RISING: wkupcr &= ~(1 << (WKUP_EDGE_SHIFT + pin_id)); break; default: return -EINVAL; } SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); if (en) stm32_pwr_irq_unmask(d); return 0; } #ifdef CONFIG_SMP static int stm32_pwr_set_affinity_parent(struct irq_data *data, const struct cpumask *dest, bool force) { struct stm32_pwr_data *priv = data->domain->host_data; struct irq_data *parent = irq_get_irq_data(priv->irq); if (parent->chip && parent->chip->irq_set_affinity) return parent->chip->irq_set_affinity(parent, dest, force); return IRQ_SET_MASK_OK_DONE; } #endif static int stm32_pwr_irq_request_resources(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; struct gpio_desc *gpio; int ret; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); gpio = gpiod_get_index(priv->dev, "wakeup", d->hwirq, GPIOD_IN); if (IS_ERR(gpio)) { ret = PTR_ERR(gpio); dev_err(priv->dev, "Failed to get wakeup gpio: %d", ret); return ret; } priv->gpio[d->hwirq] = gpio; return 0; } static void stm32_pwr_irq_release_resources(struct irq_data *d) { struct stm32_pwr_data *priv = d->domain->host_data; dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); gpiod_put(priv->gpio[d->hwirq]); } static struct irq_chip stm32_pwr_irq_chip = { .name = "stm32-pwr-irq", .irq_ack = stm32_pwr_irq_ack, .irq_mask = stm32_pwr_irq_mask, .irq_unmask = stm32_pwr_irq_unmask, .irq_set_type = stm32_pwr_irq_set_type, .irq_set_wake = stm32_pwr_irq_set_wake, .irq_request_resources = stm32_pwr_irq_request_resources, .irq_release_resources = stm32_pwr_irq_release_resources, #ifdef CONFIG_SMP .irq_set_affinity = stm32_pwr_set_affinity_parent, #endif }; static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id, enum wkup_pull_setting config) { struct stm32_pwr_data *priv = d->host_data; u32 wkupcr; dev_dbg(priv->dev, "irq:%d pull config:0x%x\n", pin_id, config); if (config >= WKUP_PULL_RESERVED) { pr_err("%s: bad irq pull config\n", __func__); return -EINVAL; } wkupcr = readl_relaxed(priv->base + WKUPCR); wkupcr &= ~((WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2)); wkupcr |= (config & WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2); SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); return 0; } static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type) { if (WARN_ON(intsize < 3)) { pr_err("%s: bad irq config parameters\n", __func__); return -EINVAL; } *out_hwirq = intspec[0]; *out_type = intspec[1] & (IRQ_TYPE_SENSE_MASK); return stm32_pwr_irq_set_pull_config(d, intspec[0], intspec[2]); } static int stm32_pwr_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *data) { struct irq_fwspec *fwspec = data; irq_hw_number_t hwirq; hwirq = fwspec->param[0]; irq_domain_set_info(d, virq, hwirq, &stm32_pwr_irq_chip, d->host_data, handle_edge_irq, NULL, NULL); return 0; } static const struct irq_domain_ops stm32_pwr_irq_domain_ops = { .alloc = stm32_pwr_alloc, .xlate = stm32_pwr_xlate, .free = irq_domain_free_irqs_common, }; /* * Handler for the cascaded IRQ. */ static void stm32_pwr_handle_irq(struct irq_desc *desc) { struct stm32_pwr_data *priv = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); u32 wkupfr, wkupenr, i; chained_irq_enter(chip, desc); wkupfr = readl_relaxed(priv->base + WKUPFR); wkupenr = readl_relaxed(priv->base + MPUWKUPENR); for (i = 0; i < NB_WAKEUPPINS; i++) { if ((wkupfr & BIT(i)) && (wkupenr & BIT(i))) { struct irq_desc *d; d = irq_to_desc(irq_find_mapping(priv->domain, i)); if (priv->wake & BIT(i)) { dev_dbg(priv->dev, "irq %d while wake enabled\n", i); priv->pending |= BIT(i); } dev_dbg(priv->dev, "handle wkup irq:%d\n", i); handle_edge_irq(d); } } chained_irq_exit(chip, desc); } static int __maybe_unused stm32_pwr_suspend(struct device *dev) { struct stm32_pwr_data *priv = dev_get_drvdata(dev); pr_debug("suspend"); if (priv->pending != 0) return -EBUSY; return 0; } static const struct dev_pm_ops stm32_pwr_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_pwr_suspend, NULL) }; static int stm32_pwr_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_pwr_data *priv; struct device_node *np = dev->of_node; struct resource *res; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; dev_set_drvdata(dev, priv); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->base)) { dev_err(dev, "Unable to map registers\n"); return PTR_ERR(priv->base); } /* Disable all wake-up pins */ SMC(STM32_SVC_PWR, STM32_WRITE, MPUWKUPENR, 0); /* Clear all interrupts flags */ SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, WKUP_FLAGS_MASK); priv->domain = irq_domain_add_linear(np, NB_WAKEUPPINS, &stm32_pwr_irq_domain_ops, priv); if (!priv->domain) { dev_err(dev, "%s: Unable to add irq domain!\n", __func__); ret = -ENOMEM; goto out; } ret = irq_of_parse_and_map(np, 0); if (ret < 0) { dev_err(dev, "failed to get PWR IRQ\n"); ret = priv->irq; goto out_domain; } priv->irq = ret; irq_set_chained_handler_and_data(priv->irq, stm32_pwr_handle_irq, priv); of_node_clear_flag(np, OF_POPULATED); return 0; out_domain: irq_domain_remove(priv->domain); out: return ret; } static int stm32_pwr_remove(struct platform_device *pdev) { struct stm32_pwr_data *priv = dev_get_drvdata(&pdev->dev); irq_domain_remove(priv->domain); return 0; } static const struct of_device_id stm32_pwr_ids[] = { { .compatible = "st,stm32mp1-pwr", }, {}, }; MODULE_DEVICE_TABLE(of, stm32_pwr_ids); static struct platform_driver stm32_pwr_driver = { .probe = stm32_pwr_probe, .remove = stm32_pwr_remove, .driver = { .name = "stm32_pwr", .of_match_table = stm32_pwr_ids, .pm = &stm32_pwr_pm, }, }; static int __init stm32_pwr_init(void) { return platform_driver_register(&stm32_pwr_driver); } static void __exit stm32_pwr_exit(void) { return platform_driver_unregister(&stm32_pwr_driver); } arch_initcall(stm32_pwr_init); module_exit(stm32_pwr_exit);