Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/meson: Add support for gpio interrupt controller

Add support for the interrupt gpio controller found on Amlogic's meson
SoC family.

This controller is a separate controller from the gpio controller. It is
able to spy on the SoC pad. It is essentially a 256 to 8 router with a
filtering block to select level or edge and polarity. The number of actual
mappable inputs depends on the SoC.

Cc: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

authored by

Jerome Brunet and committed by
Marc Zyngier
215f4cc0 df48d3b5

+423
+8
drivers/irqchip/Kconfig
··· 324 324 help 325 325 Support for the UniPhier AIDET (ARM Interrupt Detector). 326 326 327 + config MESON_IRQ_GPIO 328 + bool "Meson GPIO Interrupt Multiplexer" 329 + depends on ARCH_MESON || COMPILE_TEST 330 + select IRQ_DOMAIN 331 + select IRQ_DOMAIN_HIERARCHY 332 + help 333 + Support Meson SoC Family GPIO Interrupt Multiplexer 334 + 327 335 endmenu
+1
drivers/irqchip/Makefile
··· 79 79 obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o 80 80 obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o 81 81 obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o 82 + obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
+414
drivers/irqchip/irq-meson-gpio.c
··· 1 + /* 2 + * Copyright (c) 2015 Endless Mobile, Inc. 3 + * Author: Carlo Caione <carlo@endlessm.com> 4 + * Copyright (c) 2016 BayLibre, SAS. 5 + * Author: Jerome Brunet <jbrunet@baylibre.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of version 2 of the GNU General Public License as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but 12 + * WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 + * General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 + * The full GNU General Public License is included in this distribution 19 + * in the file called COPYING. 20 + */ 21 + 22 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 23 + 24 + #include <linux/io.h> 25 + #include <linux/module.h> 26 + #include <linux/irq.h> 27 + #include <linux/irqdomain.h> 28 + #include <linux/irqchip.h> 29 + #include <linux/of.h> 30 + #include <linux/of_address.h> 31 + 32 + #define NUM_CHANNEL 8 33 + #define MAX_INPUT_MUX 256 34 + 35 + #define REG_EDGE_POL 0x00 36 + #define REG_PIN_03_SEL 0x04 37 + #define REG_PIN_47_SEL 0x08 38 + #define REG_FILTER_SEL 0x0c 39 + 40 + #define REG_EDGE_POL_MASK(x) (BIT(x) | BIT(16 + (x))) 41 + #define REG_EDGE_POL_EDGE(x) BIT(x) 42 + #define REG_EDGE_POL_LOW(x) BIT(16 + (x)) 43 + #define REG_PIN_SEL_SHIFT(x) (((x) % 4) * 8) 44 + #define REG_FILTER_SEL_SHIFT(x) ((x) * 4) 45 + 46 + struct meson_gpio_irq_params { 47 + unsigned int nr_hwirq; 48 + }; 49 + 50 + static const struct meson_gpio_irq_params meson8b_params = { 51 + .nr_hwirq = 119, 52 + }; 53 + 54 + static const struct meson_gpio_irq_params gxbb_params = { 55 + .nr_hwirq = 133, 56 + }; 57 + 58 + static const struct meson_gpio_irq_params gxl_params = { 59 + .nr_hwirq = 110, 60 + }; 61 + 62 + static const struct of_device_id meson_irq_gpio_matches[] = { 63 + { .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params }, 64 + { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, 65 + { .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params }, 66 + { } 67 + }; 68 + 69 + struct meson_gpio_irq_controller { 70 + unsigned int nr_hwirq; 71 + void __iomem *base; 72 + u32 channel_irqs[NUM_CHANNEL]; 73 + DECLARE_BITMAP(channel_map, NUM_CHANNEL); 74 + spinlock_t lock; 75 + }; 76 + 77 + static void meson_gpio_irq_update_bits(struct meson_gpio_irq_controller *ctl, 78 + unsigned int reg, u32 mask, u32 val) 79 + { 80 + u32 tmp; 81 + 82 + tmp = readl_relaxed(ctl->base + reg); 83 + tmp &= ~mask; 84 + tmp |= val; 85 + writel_relaxed(tmp, ctl->base + reg); 86 + } 87 + 88 + static unsigned int meson_gpio_irq_channel_to_reg(unsigned int channel) 89 + { 90 + return (channel < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL; 91 + } 92 + 93 + static int 94 + meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl, 95 + unsigned long hwirq, 96 + u32 **channel_hwirq) 97 + { 98 + unsigned int reg, idx; 99 + 100 + spin_lock(&ctl->lock); 101 + 102 + /* Find a free channel */ 103 + idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL); 104 + if (idx >= NUM_CHANNEL) { 105 + spin_unlock(&ctl->lock); 106 + pr_err("No channel available\n"); 107 + return -ENOSPC; 108 + } 109 + 110 + /* Mark the channel as used */ 111 + set_bit(idx, ctl->channel_map); 112 + 113 + /* 114 + * Setup the mux of the channel to route the signal of the pad 115 + * to the appropriate input of the GIC 116 + */ 117 + reg = meson_gpio_irq_channel_to_reg(idx); 118 + meson_gpio_irq_update_bits(ctl, reg, 119 + 0xff << REG_PIN_SEL_SHIFT(idx), 120 + hwirq << REG_PIN_SEL_SHIFT(idx)); 121 + 122 + /* 123 + * Get the hwirq number assigned to this channel through 124 + * a pointer the channel_irq table. The added benifit of this 125 + * method is that we can also retrieve the channel index with 126 + * it, using the table base. 127 + */ 128 + *channel_hwirq = &(ctl->channel_irqs[idx]); 129 + 130 + spin_unlock(&ctl->lock); 131 + 132 + pr_debug("hwirq %lu assigned to channel %d - irq %u\n", 133 + hwirq, idx, **channel_hwirq); 134 + 135 + return 0; 136 + } 137 + 138 + static unsigned int 139 + meson_gpio_irq_get_channel_idx(struct meson_gpio_irq_controller *ctl, 140 + u32 *channel_hwirq) 141 + { 142 + return channel_hwirq - ctl->channel_irqs; 143 + } 144 + 145 + static void 146 + meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl, 147 + u32 *channel_hwirq) 148 + { 149 + unsigned int idx; 150 + 151 + idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); 152 + clear_bit(idx, ctl->channel_map); 153 + } 154 + 155 + static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl, 156 + unsigned int type, 157 + u32 *channel_hwirq) 158 + { 159 + u32 val = 0; 160 + unsigned int idx; 161 + 162 + idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); 163 + 164 + /* 165 + * The controller has a filter block to operate in either LEVEL or 166 + * EDGE mode, then signal is sent to the GIC. To enable LEVEL_LOW and 167 + * EDGE_FALLING support (which the GIC does not support), the filter 168 + * block is also able to invert the input signal it gets before 169 + * providing it to the GIC. 170 + */ 171 + type &= IRQ_TYPE_SENSE_MASK; 172 + 173 + if (type == IRQ_TYPE_EDGE_BOTH) 174 + return -EINVAL; 175 + 176 + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) 177 + val |= REG_EDGE_POL_EDGE(idx); 178 + 179 + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) 180 + val |= REG_EDGE_POL_LOW(idx); 181 + 182 + spin_lock(&ctl->lock); 183 + 184 + meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, 185 + REG_EDGE_POL_MASK(idx), val); 186 + 187 + spin_unlock(&ctl->lock); 188 + 189 + return 0; 190 + } 191 + 192 + static unsigned int meson_gpio_irq_type_output(unsigned int type) 193 + { 194 + unsigned int sense = type & IRQ_TYPE_SENSE_MASK; 195 + 196 + type &= ~IRQ_TYPE_SENSE_MASK; 197 + 198 + /* 199 + * The polarity of the signal provided to the GIC should always 200 + * be high. 201 + */ 202 + if (sense & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) 203 + type |= IRQ_TYPE_LEVEL_HIGH; 204 + else if (sense & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) 205 + type |= IRQ_TYPE_EDGE_RISING; 206 + 207 + return type; 208 + } 209 + 210 + static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type) 211 + { 212 + struct meson_gpio_irq_controller *ctl = data->domain->host_data; 213 + u32 *channel_hwirq = irq_data_get_irq_chip_data(data); 214 + int ret; 215 + 216 + ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq); 217 + if (ret) 218 + return ret; 219 + 220 + return irq_chip_set_type_parent(data, 221 + meson_gpio_irq_type_output(type)); 222 + } 223 + 224 + static struct irq_chip meson_gpio_irq_chip = { 225 + .name = "meson-gpio-irqchip", 226 + .irq_mask = irq_chip_mask_parent, 227 + .irq_unmask = irq_chip_unmask_parent, 228 + .irq_eoi = irq_chip_eoi_parent, 229 + .irq_set_type = meson_gpio_irq_set_type, 230 + .irq_retrigger = irq_chip_retrigger_hierarchy, 231 + #ifdef CONFIG_SMP 232 + .irq_set_affinity = irq_chip_set_affinity_parent, 233 + #endif 234 + .flags = IRQCHIP_SET_TYPE_MASKED, 235 + }; 236 + 237 + static int meson_gpio_irq_domain_translate(struct irq_domain *domain, 238 + struct irq_fwspec *fwspec, 239 + unsigned long *hwirq, 240 + unsigned int *type) 241 + { 242 + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { 243 + *hwirq = fwspec->param[0]; 244 + *type = fwspec->param[1]; 245 + return 0; 246 + } 247 + 248 + return -EINVAL; 249 + } 250 + 251 + static int meson_gpio_irq_allocate_gic_irq(struct irq_domain *domain, 252 + unsigned int virq, 253 + u32 hwirq, 254 + unsigned int type) 255 + { 256 + struct irq_fwspec fwspec; 257 + 258 + fwspec.fwnode = domain->parent->fwnode; 259 + fwspec.param_count = 3; 260 + fwspec.param[0] = 0; /* SPI */ 261 + fwspec.param[1] = hwirq; 262 + fwspec.param[2] = meson_gpio_irq_type_output(type); 263 + 264 + return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 265 + } 266 + 267 + static int meson_gpio_irq_domain_alloc(struct irq_domain *domain, 268 + unsigned int virq, 269 + unsigned int nr_irqs, 270 + void *data) 271 + { 272 + struct irq_fwspec *fwspec = data; 273 + struct meson_gpio_irq_controller *ctl = domain->host_data; 274 + unsigned long hwirq; 275 + u32 *channel_hwirq; 276 + unsigned int type; 277 + int ret; 278 + 279 + if (WARN_ON(nr_irqs != 1)) 280 + return -EINVAL; 281 + 282 + ret = meson_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type); 283 + if (ret) 284 + return ret; 285 + 286 + ret = meson_gpio_irq_request_channel(ctl, hwirq, &channel_hwirq); 287 + if (ret) 288 + return ret; 289 + 290 + ret = meson_gpio_irq_allocate_gic_irq(domain, virq, 291 + *channel_hwirq, type); 292 + if (ret < 0) { 293 + pr_err("failed to allocate gic irq %u\n", *channel_hwirq); 294 + meson_gpio_irq_release_channel(ctl, channel_hwirq); 295 + return ret; 296 + } 297 + 298 + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 299 + &meson_gpio_irq_chip, channel_hwirq); 300 + 301 + return 0; 302 + } 303 + 304 + static void meson_gpio_irq_domain_free(struct irq_domain *domain, 305 + unsigned int virq, 306 + unsigned int nr_irqs) 307 + { 308 + struct meson_gpio_irq_controller *ctl = domain->host_data; 309 + struct irq_data *irq_data; 310 + u32 *channel_hwirq; 311 + 312 + if (WARN_ON(nr_irqs != 1)) 313 + return; 314 + 315 + irq_domain_free_irqs_parent(domain, virq, 1); 316 + 317 + irq_data = irq_domain_get_irq_data(domain, virq); 318 + channel_hwirq = irq_data_get_irq_chip_data(irq_data); 319 + 320 + meson_gpio_irq_release_channel(ctl, channel_hwirq); 321 + } 322 + 323 + static const struct irq_domain_ops meson_gpio_irq_domain_ops = { 324 + .alloc = meson_gpio_irq_domain_alloc, 325 + .free = meson_gpio_irq_domain_free, 326 + .translate = meson_gpio_irq_domain_translate, 327 + }; 328 + 329 + static int __init meson_gpio_irq_parse_dt(struct device_node *node, 330 + struct meson_gpio_irq_controller *ctl) 331 + { 332 + const struct of_device_id *match; 333 + const struct meson_gpio_irq_params *params; 334 + int ret; 335 + 336 + match = of_match_node(meson_irq_gpio_matches, node); 337 + if (!match) 338 + return -ENODEV; 339 + 340 + params = match->data; 341 + ctl->nr_hwirq = params->nr_hwirq; 342 + 343 + ret = of_property_read_variable_u32_array(node, 344 + "amlogic,channel-interrupts", 345 + ctl->channel_irqs, 346 + NUM_CHANNEL, 347 + NUM_CHANNEL); 348 + if (ret < 0) { 349 + pr_err("can't get %d channel interrupts\n", NUM_CHANNEL); 350 + return ret; 351 + } 352 + 353 + return 0; 354 + } 355 + 356 + static int __init meson_gpio_irq_of_init(struct device_node *node, 357 + struct device_node *parent) 358 + { 359 + struct irq_domain *domain, *parent_domain; 360 + struct meson_gpio_irq_controller *ctl; 361 + int ret; 362 + 363 + if (!parent) { 364 + pr_err("missing parent interrupt node\n"); 365 + return -ENODEV; 366 + } 367 + 368 + parent_domain = irq_find_host(parent); 369 + if (!parent_domain) { 370 + pr_err("unable to obtain parent domain\n"); 371 + return -ENXIO; 372 + } 373 + 374 + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 375 + if (!ctl) 376 + return -ENOMEM; 377 + 378 + spin_lock_init(&ctl->lock); 379 + 380 + ctl->base = of_iomap(node, 0); 381 + if (!ctl->base) { 382 + ret = -ENOMEM; 383 + goto free_ctl; 384 + } 385 + 386 + ret = meson_gpio_irq_parse_dt(node, ctl); 387 + if (ret) 388 + goto free_channel_irqs; 389 + 390 + domain = irq_domain_create_hierarchy(parent_domain, 0, ctl->nr_hwirq, 391 + of_node_to_fwnode(node), 392 + &meson_gpio_irq_domain_ops, 393 + ctl); 394 + if (!domain) { 395 + pr_err("failed to add domain\n"); 396 + ret = -ENODEV; 397 + goto free_channel_irqs; 398 + } 399 + 400 + pr_info("%d to %d gpio interrupt mux initialized\n", 401 + ctl->nr_hwirq, NUM_CHANNEL); 402 + 403 + return 0; 404 + 405 + free_channel_irqs: 406 + iounmap(ctl->base); 407 + free_ctl: 408 + kfree(ctl); 409 + 410 + return ret; 411 + } 412 + 413 + IRQCHIP_DECLARE(meson_gpio_intc, "amlogic,meson-gpio-intc", 414 + meson_gpio_irq_of_init);