335 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * AppliedMicro X-Gene SoC GPIO-Standby Driver4 *5 * Copyright (c) 2014, Applied Micro Circuits Corporation6 * Author: Tin Huynh <tnhuynh@apm.com>.7 * Y Vo <yvo@apm.com>.8 * Quan Nguyen <qnguyen@apm.com>.9 */10 11#include <linux/module.h>12#include <linux/io.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/gpio/driver.h>16#include <linux/acpi.h>17 18#include "gpiolib-acpi.h"19 20/* Common property names */21#define XGENE_NIRQ_PROPERTY "apm,nr-irqs"22#define XGENE_NGPIO_PROPERTY "apm,nr-gpios"23#define XGENE_IRQ_START_PROPERTY "apm,irq-start"24 25#define XGENE_DFLT_MAX_NGPIO 2226#define XGENE_DFLT_MAX_NIRQ 627#define XGENE_DFLT_IRQ_START_PIN 828#define GPIO_MASK(x) (1U << ((x) % 32))29 30#define MPA_GPIO_INT_LVL 0x029031#define MPA_GPIO_OE_ADDR 0x029c32#define MPA_GPIO_OUT_ADDR 0x02a033#define MPA_GPIO_IN_ADDR 0x02a434#define MPA_GPIO_SEL_LO 0x029435 36#define GPIO_INT_LEVEL_H 0x00000137#define GPIO_INT_LEVEL_L 0x00000038 39/**40 * struct xgene_gpio_sb - GPIO-Standby private data structure.41 * @gc: memory-mapped GPIO controllers.42 * @regs: GPIO register base offset43 * @irq_domain: GPIO interrupt domain44 * @irq_start: GPIO pin that start support interrupt45 * @nirq: Number of GPIO pins that supports interrupt46 * @parent_irq_base: Start parent HWIRQ47 */48struct xgene_gpio_sb {49 struct gpio_chip gc;50 void __iomem *regs;51 struct irq_domain *irq_domain;52 u16 irq_start;53 u16 nirq;54 u16 parent_irq_base;55};56 57#define HWIRQ_TO_GPIO(priv, hwirq) ((hwirq) + (priv)->irq_start)58#define GPIO_TO_HWIRQ(priv, gpio) ((gpio) - (priv)->irq_start)59 60static void xgene_gpio_set_bit(struct gpio_chip *gc,61 void __iomem *reg, u32 gpio, int val)62{63 u32 data;64 65 data = gc->read_reg(reg);66 if (val)67 data |= GPIO_MASK(gpio);68 else69 data &= ~GPIO_MASK(gpio);70 gc->write_reg(reg, data);71}72 73static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)74{75 struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d);76 int gpio = HWIRQ_TO_GPIO(priv, d->hwirq);77 int lvl_type = GPIO_INT_LEVEL_H;78 79 switch (type & IRQ_TYPE_SENSE_MASK) {80 case IRQ_TYPE_EDGE_RISING:81 case IRQ_TYPE_LEVEL_HIGH:82 lvl_type = GPIO_INT_LEVEL_H;83 break;84 case IRQ_TYPE_EDGE_FALLING:85 case IRQ_TYPE_LEVEL_LOW:86 lvl_type = GPIO_INT_LEVEL_L;87 break;88 default:89 break;90 }91 92 xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,93 gpio * 2, 1);94 xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_INT_LVL,95 d->hwirq, lvl_type);96 97 /* Propagate IRQ type setting to parent */98 if (type & IRQ_TYPE_EDGE_BOTH)99 return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING);100 else101 return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);102}103 104static struct irq_chip xgene_gpio_sb_irq_chip = {105 .name = "sbgpio",106 .irq_eoi = irq_chip_eoi_parent,107 .irq_mask = irq_chip_mask_parent,108 .irq_unmask = irq_chip_unmask_parent,109 .irq_set_type = xgene_gpio_sb_irq_set_type,110};111 112static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)113{114 struct xgene_gpio_sb *priv = gpiochip_get_data(gc);115 struct irq_fwspec fwspec;116 117 if ((gpio < priv->irq_start) ||118 (gpio > HWIRQ_TO_GPIO(priv, priv->nirq)))119 return -ENXIO;120 121 fwspec.fwnode = gc->parent->fwnode;122 fwspec.param_count = 2;123 fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);124 fwspec.param[1] = IRQ_TYPE_EDGE_RISING;125 return irq_create_fwspec_mapping(&fwspec);126}127 128static int xgene_gpio_sb_domain_activate(struct irq_domain *d,129 struct irq_data *irq_data,130 bool reserve)131{132 struct xgene_gpio_sb *priv = d->host_data;133 u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);134 int ret;135 136 ret = gpiochip_lock_as_irq(&priv->gc, gpio);137 if (ret) {138 dev_err(priv->gc.parent,139 "Unable to configure XGene GPIO standby pin %d as IRQ\n",140 gpio);141 return ret;142 }143 144 xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,145 gpio * 2, 1);146 return 0;147}148 149static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d,150 struct irq_data *irq_data)151{152 struct xgene_gpio_sb *priv = d->host_data;153 u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);154 155 gpiochip_unlock_as_irq(&priv->gc, gpio);156 xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,157 gpio * 2, 0);158}159 160static int xgene_gpio_sb_domain_translate(struct irq_domain *d,161 struct irq_fwspec *fwspec,162 unsigned long *hwirq,163 unsigned int *type)164{165 struct xgene_gpio_sb *priv = d->host_data;166 167 if ((fwspec->param_count != 2) ||168 (fwspec->param[0] >= priv->nirq))169 return -EINVAL;170 *hwirq = fwspec->param[0];171 *type = fwspec->param[1];172 return 0;173}174 175static int xgene_gpio_sb_domain_alloc(struct irq_domain *domain,176 unsigned int virq,177 unsigned int nr_irqs, void *data)178{179 struct irq_fwspec *fwspec = data;180 struct irq_fwspec parent_fwspec;181 struct xgene_gpio_sb *priv = domain->host_data;182 irq_hw_number_t hwirq;183 unsigned int i;184 185 hwirq = fwspec->param[0];186 for (i = 0; i < nr_irqs; i++)187 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,188 &xgene_gpio_sb_irq_chip, priv);189 190 parent_fwspec.fwnode = domain->parent->fwnode;191 if (is_of_node(parent_fwspec.fwnode)) {192 parent_fwspec.param_count = 3;193 parent_fwspec.param[0] = 0;/* SPI */194 /* Skip SGIs and PPIs*/195 parent_fwspec.param[1] = hwirq + priv->parent_irq_base - 32;196 parent_fwspec.param[2] = fwspec->param[1];197 } else if (is_fwnode_irqchip(parent_fwspec.fwnode)) {198 parent_fwspec.param_count = 2;199 parent_fwspec.param[0] = hwirq + priv->parent_irq_base;200 parent_fwspec.param[1] = fwspec->param[1];201 } else202 return -EINVAL;203 204 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,205 &parent_fwspec);206}207 208static const struct irq_domain_ops xgene_gpio_sb_domain_ops = {209 .translate = xgene_gpio_sb_domain_translate,210 .alloc = xgene_gpio_sb_domain_alloc,211 .free = irq_domain_free_irqs_common,212 .activate = xgene_gpio_sb_domain_activate,213 .deactivate = xgene_gpio_sb_domain_deactivate,214};215 216static int xgene_gpio_sb_probe(struct platform_device *pdev)217{218 struct xgene_gpio_sb *priv;219 int ret;220 void __iomem *regs;221 struct irq_domain *parent_domain = NULL;222 u32 val32;223 224 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);225 if (!priv)226 return -ENOMEM;227 228 regs = devm_platform_ioremap_resource(pdev, 0);229 if (IS_ERR(regs))230 return PTR_ERR(regs);231 232 priv->regs = regs;233 234 ret = platform_get_irq(pdev, 0);235 if (ret > 0) {236 priv->parent_irq_base = irq_get_irq_data(ret)->hwirq;237 parent_domain = irq_get_irq_data(ret)->domain;238 }239 if (!parent_domain) {240 dev_err(&pdev->dev, "unable to obtain parent domain\n");241 return -ENODEV;242 }243 244 ret = bgpio_init(&priv->gc, &pdev->dev, 4,245 regs + MPA_GPIO_IN_ADDR,246 regs + MPA_GPIO_OUT_ADDR, NULL,247 regs + MPA_GPIO_OE_ADDR, NULL, 0);248 if (ret)249 return ret;250 251 priv->gc.to_irq = xgene_gpio_sb_to_irq;252 253 /* Retrieve start irq pin, use default if property not found */254 priv->irq_start = XGENE_DFLT_IRQ_START_PIN;255 if (!device_property_read_u32(&pdev->dev,256 XGENE_IRQ_START_PROPERTY, &val32))257 priv->irq_start = val32;258 259 /* Retrieve number irqs, use default if property not found */260 priv->nirq = XGENE_DFLT_MAX_NIRQ;261 if (!device_property_read_u32(&pdev->dev, XGENE_NIRQ_PROPERTY, &val32))262 priv->nirq = val32;263 264 /* Retrieve number gpio, use default if property not found */265 priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO;266 if (!device_property_read_u32(&pdev->dev, XGENE_NGPIO_PROPERTY, &val32))267 priv->gc.ngpio = val32;268 269 dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n",270 priv->gc.ngpio, priv->nirq, priv->irq_start);271 272 platform_set_drvdata(pdev, priv);273 274 priv->irq_domain = irq_domain_create_hierarchy(parent_domain,275 0, priv->nirq, pdev->dev.fwnode,276 &xgene_gpio_sb_domain_ops, priv);277 if (!priv->irq_domain)278 return -ENODEV;279 280 priv->gc.irq.domain = priv->irq_domain;281 282 ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);283 if (ret) {284 dev_err(&pdev->dev,285 "failed to register X-Gene GPIO Standby driver\n");286 irq_domain_remove(priv->irq_domain);287 return ret;288 }289 290 dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");291 292 /* Register interrupt handlers for GPIO signaled ACPI Events */293 acpi_gpiochip_request_interrupts(&priv->gc);294 295 return ret;296}297 298static void xgene_gpio_sb_remove(struct platform_device *pdev)299{300 struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);301 302 acpi_gpiochip_free_interrupts(&priv->gc);303 304 irq_domain_remove(priv->irq_domain);305}306 307static const struct of_device_id xgene_gpio_sb_of_match[] = {308 {.compatible = "apm,xgene-gpio-sb", },309 {},310};311MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match);312 313#ifdef CONFIG_ACPI314static const struct acpi_device_id xgene_gpio_sb_acpi_match[] = {315 {"APMC0D15", 0},316 {},317};318MODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match);319#endif320 321static struct platform_driver xgene_gpio_sb_driver = {322 .driver = {323 .name = "xgene-gpio-sb",324 .of_match_table = xgene_gpio_sb_of_match,325 .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match),326 },327 .probe = xgene_gpio_sb_probe,328 .remove_new = xgene_gpio_sb_remove,329};330module_platform_driver(xgene_gpio_sb_driver);331 332MODULE_AUTHOR("AppliedMicro");333MODULE_DESCRIPTION("APM X-Gene GPIO Standby driver");334MODULE_LICENSE("GPL");335