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

pinctrl: sunxi: Implement multiple interrupt banks support

The A23 and A31 support multiple interrupt banks. Support it by adding a linear
domain covering all the banks. It's trickier than it should because there's an
interrupt per bank, so we have multiple interrupts using the same domain.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Maxime Ripard and committed by
Linus Walleij
aebdc8ab c11a33c1

+76 -23
+51 -14
drivers/pinctrl/sunxi/pinctrl-sunxi.c
··· 636 636 { 637 637 struct irq_chip *chip = irq_get_chip(irq); 638 638 struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); 639 - const unsigned long reg = readl(pctl->membase + IRQ_STATUS_REG); 639 + unsigned long bank, reg, val; 640 + 641 + for (bank = 0; bank < pctl->desc->irq_banks; bank++) 642 + if (irq == pctl->irq[bank]) 643 + break; 644 + 645 + if (bank == pctl->desc->irq_banks) 646 + return; 647 + 648 + reg = sunxi_irq_status_reg_from_bank(bank); 649 + val = readl(pctl->membase + reg); 640 650 641 651 /* Clear all interrupts */ 642 - writel(reg, pctl->membase + IRQ_STATUS_REG); 652 + writel(val, pctl->membase + reg); 643 653 644 - if (reg) { 654 + if (val) { 645 655 int irqoffset; 646 656 647 657 chained_irq_enter(chip, desc); 648 - for_each_set_bit(irqoffset, &reg, SUNXI_IRQ_NUMBER) { 649 - int pin_irq = irq_find_mapping(pctl->domain, irqoffset); 658 + for_each_set_bit(irqoffset, &val, IRQ_PER_BANK) { 659 + int pin_irq = irq_find_mapping(pctl->domain, 660 + bank * IRQ_PER_BANK + irqoffset); 650 661 generic_handle_irq(pin_irq); 651 662 } 652 663 chained_irq_exit(chip, desc); ··· 725 714 726 715 while (func->name) { 727 716 /* Create interrupt mapping while we're at it */ 728 - if (!strcmp(func->name, "irq")) 729 - pctl->irq_array[func->irqnum] = pin->pin.number; 717 + if (!strcmp(func->name, "irq")) { 718 + int irqnum = func->irqnum + func->irqbank * IRQ_PER_BANK; 719 + pctl->irq_array[irqnum] = pin->pin.number; 720 + } 721 + 730 722 sunxi_pinctrl_add_function(pctl, func->name); 731 723 func++; 732 724 } ··· 798 784 799 785 pctl->dev = &pdev->dev; 800 786 pctl->desc = desc; 787 + 788 + pctl->irq_array = devm_kcalloc(&pdev->dev, 789 + IRQ_PER_BANK * pctl->desc->irq_banks, 790 + sizeof(*pctl->irq_array), 791 + GFP_KERNEL); 792 + if (!pctl->irq_array) 793 + return -ENOMEM; 801 794 802 795 ret = sunxi_pinctrl_build_state(pdev); 803 796 if (ret) { ··· 890 869 if (ret) 891 870 goto gpiochip_error; 892 871 893 - pctl->irq = irq_of_parse_and_map(node, 0); 872 + pctl->irq = devm_kcalloc(&pdev->dev, 873 + pctl->desc->irq_banks, 874 + sizeof(*pctl->irq), 875 + GFP_KERNEL); 894 876 if (!pctl->irq) { 895 - ret = -EINVAL; 877 + ret = -ENOMEM; 896 878 goto clk_error; 897 879 } 898 880 899 - pctl->domain = irq_domain_add_linear(node, SUNXI_IRQ_NUMBER, 900 - &irq_domain_simple_ops, NULL); 881 + for (i = 0; i < pctl->desc->irq_banks; i++) { 882 + pctl->irq[i] = platform_get_irq(pdev, i); 883 + if (pctl->irq[i] < 0) { 884 + ret = pctl->irq[i]; 885 + goto clk_error; 886 + } 887 + } 888 + 889 + pctl->domain = irq_domain_add_linear(node, 890 + pctl->desc->irq_banks * IRQ_PER_BANK, 891 + &irq_domain_simple_ops, 892 + NULL); 901 893 if (!pctl->domain) { 902 894 dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); 903 895 ret = -ENOMEM; 904 896 goto clk_error; 905 897 } 906 898 907 - for (i = 0; i < SUNXI_IRQ_NUMBER; i++) { 899 + for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) { 908 900 int irqno = irq_create_mapping(pctl->domain, i); 909 901 910 902 irq_set_chip_and_handler(irqno, &sunxi_pinctrl_irq_chip, ··· 925 891 irq_set_chip_data(irqno, pctl); 926 892 }; 927 893 928 - irq_set_chained_handler(pctl->irq, sunxi_pinctrl_irq_handler); 929 - irq_set_handler_data(pctl->irq, pctl); 894 + for (i = 0; i < pctl->desc->irq_banks; i++) { 895 + irq_set_chained_handler(pctl->irq[i], 896 + sunxi_pinctrl_irq_handler); 897 + irq_set_handler_data(pctl->irq[i], pctl); 898 + } 930 899 931 900 dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); 932 901
+25 -9
drivers/pinctrl/sunxi/pinctrl-sunxi.h
··· 53 53 #define PULL_PINS_BITS 2 54 54 #define PULL_PINS_MASK 0x03 55 55 56 - #define SUNXI_IRQ_NUMBER 32 56 + #define IRQ_PER_BANK 32 57 57 58 58 #define IRQ_CFG_REG 0x200 59 59 #define IRQ_CFG_IRQ_PER_REG 8 ··· 67 67 #define IRQ_STATUS_IRQ_PER_REG 32 68 68 #define IRQ_STATUS_IRQ_BITS 1 69 69 #define IRQ_STATUS_IRQ_MASK ((1 << IRQ_STATUS_IRQ_BITS) - 1) 70 + 71 + #define IRQ_MEM_SIZE 0x20 70 72 71 73 #define IRQ_EDGE_RISING 0x00 72 74 #define IRQ_EDGE_FALLING 0x01 ··· 117 115 unsigned nfunctions; 118 116 struct sunxi_pinctrl_group *groups; 119 117 unsigned ngroups; 120 - int irq; 121 - int irq_array[SUNXI_IRQ_NUMBER]; 118 + int *irq; 119 + unsigned *irq_array; 122 120 spinlock_t lock; 123 121 struct pinctrl_dev *pctl_dev; 124 122 }; ··· 230 228 231 229 static inline u32 sunxi_irq_cfg_reg(u16 irq) 232 230 { 233 - u8 reg = irq / IRQ_CFG_IRQ_PER_REG * 0x04; 234 - return reg + IRQ_CFG_REG; 231 + u8 bank = irq / IRQ_PER_BANK; 232 + u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04; 233 + 234 + return IRQ_CFG_REG + bank * IRQ_MEM_SIZE + reg; 235 235 } 236 236 237 237 static inline u32 sunxi_irq_cfg_offset(u16 irq) ··· 242 238 return irq_num * IRQ_CFG_IRQ_BITS; 243 239 } 244 240 241 + static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank) 242 + { 243 + return IRQ_CTRL_REG + bank * IRQ_MEM_SIZE; 244 + } 245 + 245 246 static inline u32 sunxi_irq_ctrl_reg(u16 irq) 246 247 { 247 - u8 reg = irq / IRQ_CTRL_IRQ_PER_REG * 0x04; 248 - return reg + IRQ_CTRL_REG; 248 + u8 bank = irq / IRQ_PER_BANK; 249 + 250 + return sunxi_irq_ctrl_reg_from_bank(bank); 249 251 } 250 252 251 253 static inline u32 sunxi_irq_ctrl_offset(u16 irq) ··· 260 250 return irq_num * IRQ_CTRL_IRQ_BITS; 261 251 } 262 252 253 + static inline u32 sunxi_irq_status_reg_from_bank(u8 bank) 254 + { 255 + return IRQ_STATUS_REG + bank * IRQ_MEM_SIZE; 256 + } 257 + 263 258 static inline u32 sunxi_irq_status_reg(u16 irq) 264 259 { 265 - u8 reg = irq / IRQ_STATUS_IRQ_PER_REG * 0x04; 266 - return reg + IRQ_STATUS_REG; 260 + u8 bank = irq / IRQ_PER_BANK; 261 + 262 + return sunxi_irq_status_reg_from_bank(bank); 267 263 } 268 264 269 265 static inline u32 sunxi_irq_status_offset(u16 irq)