diff options
Diffstat (limited to 'drivers/pinctrl/sunxi/pinctrl-sunxi.c')
| -rw-r--r-- | drivers/pinctrl/sunxi/pinctrl-sunxi.c | 187 |
1 files changed, 138 insertions, 49 deletions
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 5f38c7f67834..3df66e366c87 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c | |||
| @@ -31,6 +31,9 @@ | |||
| 31 | #include "../core.h" | 31 | #include "../core.h" |
| 32 | #include "pinctrl-sunxi.h" | 32 | #include "pinctrl-sunxi.h" |
| 33 | 33 | ||
| 34 | static struct irq_chip sunxi_pinctrl_edge_irq_chip; | ||
| 35 | static struct irq_chip sunxi_pinctrl_level_irq_chip; | ||
| 36 | |||
| 34 | static struct sunxi_pinctrl_group * | 37 | static struct sunxi_pinctrl_group * |
| 35 | sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) | 38 | sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) |
| 36 | { | 39 | { |
| @@ -508,7 +511,7 @@ static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, | |||
| 508 | base = PINS_PER_BANK * gpiospec->args[0]; | 511 | base = PINS_PER_BANK * gpiospec->args[0]; |
| 509 | pin = base + gpiospec->args[1]; | 512 | pin = base + gpiospec->args[1]; |
| 510 | 513 | ||
| 511 | if (pin > (gc->base + gc->ngpio)) | 514 | if (pin > gc->ngpio) |
| 512 | return -EINVAL; | 515 | return -EINVAL; |
| 513 | 516 | ||
| 514 | if (flags) | 517 | if (flags) |
| @@ -521,25 +524,61 @@ static int sunxi_pinctrl_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | |||
| 521 | { | 524 | { |
| 522 | struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); | 525 | struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); |
| 523 | struct sunxi_desc_function *desc; | 526 | struct sunxi_desc_function *desc; |
| 527 | unsigned pinnum = pctl->desc->pin_base + offset; | ||
| 528 | unsigned irqnum; | ||
| 524 | 529 | ||
| 525 | if (offset >= chip->ngpio) | 530 | if (offset >= chip->ngpio) |
| 526 | return -ENXIO; | 531 | return -ENXIO; |
| 527 | 532 | ||
| 528 | desc = sunxi_pinctrl_desc_find_function_by_pin(pctl, offset, "irq"); | 533 | desc = sunxi_pinctrl_desc_find_function_by_pin(pctl, pinnum, "irq"); |
| 529 | if (!desc) | 534 | if (!desc) |
| 530 | return -EINVAL; | 535 | return -EINVAL; |
| 531 | 536 | ||
| 537 | irqnum = desc->irqbank * IRQ_PER_BANK + desc->irqnum; | ||
| 538 | |||
| 532 | dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", | 539 | dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", |
| 533 | chip->label, offset + chip->base, desc->irqnum); | 540 | chip->label, offset + chip->base, irqnum); |
| 534 | 541 | ||
| 535 | return irq_find_mapping(pctl->domain, desc->irqnum); | 542 | return irq_find_mapping(pctl->domain, irqnum); |
| 536 | } | 543 | } |
| 537 | 544 | ||
| 545 | static int sunxi_pinctrl_irq_request_resources(struct irq_data *d) | ||
| 546 | { | ||
| 547 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); | ||
| 548 | struct sunxi_desc_function *func; | ||
| 549 | int ret; | ||
| 550 | |||
| 551 | func = sunxi_pinctrl_desc_find_function_by_pin(pctl, | ||
| 552 | pctl->irq_array[d->hwirq], "irq"); | ||
| 553 | if (!func) | ||
| 554 | return -EINVAL; | ||
| 555 | |||
| 556 | ret = gpio_lock_as_irq(pctl->chip, | ||
| 557 | pctl->irq_array[d->hwirq] - pctl->desc->pin_base); | ||
| 558 | if (ret) { | ||
| 559 | dev_err(pctl->dev, "unable to lock HW IRQ %lu for IRQ\n", | ||
| 560 | irqd_to_hwirq(d)); | ||
| 561 | return ret; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* Change muxing to INT mode */ | ||
| 565 | sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); | ||
| 538 | 566 | ||
| 539 | static int sunxi_pinctrl_irq_set_type(struct irq_data *d, | 567 | return 0; |
| 540 | unsigned int type) | 568 | } |
| 569 | |||
| 570 | static void sunxi_pinctrl_irq_release_resources(struct irq_data *d) | ||
| 571 | { | ||
| 572 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); | ||
| 573 | |||
| 574 | gpio_unlock_as_irq(pctl->chip, | ||
| 575 | pctl->irq_array[d->hwirq] - pctl->desc->pin_base); | ||
| 576 | } | ||
| 577 | |||
| 578 | static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) | ||
| 541 | { | 579 | { |
| 542 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); | 580 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); |
| 581 | struct irq_desc *desc = container_of(d, struct irq_desc, irq_data); | ||
| 543 | u32 reg = sunxi_irq_cfg_reg(d->hwirq); | 582 | u32 reg = sunxi_irq_cfg_reg(d->hwirq); |
| 544 | u8 index = sunxi_irq_cfg_offset(d->hwirq); | 583 | u8 index = sunxi_irq_cfg_offset(d->hwirq); |
| 545 | unsigned long flags; | 584 | unsigned long flags; |
| @@ -566,6 +605,14 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, | |||
| 566 | return -EINVAL; | 605 | return -EINVAL; |
| 567 | } | 606 | } |
| 568 | 607 | ||
| 608 | if (type & IRQ_TYPE_LEVEL_MASK) { | ||
| 609 | d->chip = &sunxi_pinctrl_level_irq_chip; | ||
| 610 | desc->handle_irq = handle_fasteoi_irq; | ||
| 611 | } else { | ||
| 612 | d->chip = &sunxi_pinctrl_edge_irq_chip; | ||
| 613 | desc->handle_irq = handle_edge_irq; | ||
| 614 | } | ||
| 615 | |||
| 569 | spin_lock_irqsave(&pctl->lock, flags); | 616 | spin_lock_irqsave(&pctl->lock, flags); |
| 570 | 617 | ||
| 571 | regval = readl(pctl->membase + reg); | 618 | regval = readl(pctl->membase + reg); |
| @@ -577,26 +624,14 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, | |||
| 577 | return 0; | 624 | return 0; |
| 578 | } | 625 | } |
| 579 | 626 | ||
| 580 | static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d) | 627 | static void sunxi_pinctrl_irq_ack(struct irq_data *d) |
| 581 | { | 628 | { |
| 582 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); | 629 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); |
| 583 | u32 ctrl_reg = sunxi_irq_ctrl_reg(d->hwirq); | ||
| 584 | u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq); | ||
| 585 | u32 status_reg = sunxi_irq_status_reg(d->hwirq); | 630 | u32 status_reg = sunxi_irq_status_reg(d->hwirq); |
| 586 | u8 status_idx = sunxi_irq_status_offset(d->hwirq); | 631 | u8 status_idx = sunxi_irq_status_offset(d->hwirq); |
| 587 | unsigned long flags; | ||
| 588 | u32 val; | ||
| 589 | |||
| 590 | spin_lock_irqsave(&pctl->lock, flags); | ||
| 591 | |||
| 592 | /* Mask the IRQ */ | ||
| 593 | val = readl(pctl->membase + ctrl_reg); | ||
| 594 | writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg); | ||
| 595 | 632 | ||
| 596 | /* Clear the IRQ */ | 633 | /* Clear the IRQ */ |
| 597 | writel(1 << status_idx, pctl->membase + status_reg); | 634 | writel(1 << status_idx, pctl->membase + status_reg); |
| 598 | |||
| 599 | spin_unlock_irqrestore(&pctl->lock, flags); | ||
| 600 | } | 635 | } |
| 601 | 636 | ||
| 602 | static void sunxi_pinctrl_irq_mask(struct irq_data *d) | 637 | static void sunxi_pinctrl_irq_mask(struct irq_data *d) |
| @@ -619,19 +654,11 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) | |||
| 619 | static void sunxi_pinctrl_irq_unmask(struct irq_data *d) | 654 | static void sunxi_pinctrl_irq_unmask(struct irq_data *d) |
| 620 | { | 655 | { |
| 621 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); | 656 | struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); |
| 622 | struct sunxi_desc_function *func; | ||
| 623 | u32 reg = sunxi_irq_ctrl_reg(d->hwirq); | 657 | u32 reg = sunxi_irq_ctrl_reg(d->hwirq); |
| 624 | u8 idx = sunxi_irq_ctrl_offset(d->hwirq); | 658 | u8 idx = sunxi_irq_ctrl_offset(d->hwirq); |
| 625 | unsigned long flags; | 659 | unsigned long flags; |
| 626 | u32 val; | 660 | u32 val; |
| 627 | 661 | ||
| 628 | func = sunxi_pinctrl_desc_find_function_by_pin(pctl, | ||
| 629 | pctl->irq_array[d->hwirq], | ||
| 630 | "irq"); | ||
| 631 | |||
| 632 | /* Change muxing to INT mode */ | ||
| 633 | sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); | ||
| 634 | |||
| 635 | spin_lock_irqsave(&pctl->lock, flags); | 662 | spin_lock_irqsave(&pctl->lock, flags); |
| 636 | 663 | ||
| 637 | /* Unmask the IRQ */ | 664 | /* Unmask the IRQ */ |
| @@ -641,28 +668,60 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) | |||
| 641 | spin_unlock_irqrestore(&pctl->lock, flags); | 668 | spin_unlock_irqrestore(&pctl->lock, flags); |
| 642 | } | 669 | } |
| 643 | 670 | ||
| 644 | static struct irq_chip sunxi_pinctrl_irq_chip = { | 671 | static void sunxi_pinctrl_irq_ack_unmask(struct irq_data *d) |
| 672 | { | ||
| 673 | sunxi_pinctrl_irq_ack(d); | ||
| 674 | sunxi_pinctrl_irq_unmask(d); | ||
| 675 | } | ||
| 676 | |||
| 677 | static struct irq_chip sunxi_pinctrl_edge_irq_chip = { | ||
| 678 | .irq_ack = sunxi_pinctrl_irq_ack, | ||
| 679 | .irq_mask = sunxi_pinctrl_irq_mask, | ||
| 680 | .irq_unmask = sunxi_pinctrl_irq_unmask, | ||
| 681 | .irq_request_resources = sunxi_pinctrl_irq_request_resources, | ||
| 682 | .irq_release_resources = sunxi_pinctrl_irq_release_resources, | ||
| 683 | .irq_set_type = sunxi_pinctrl_irq_set_type, | ||
| 684 | .flags = IRQCHIP_SKIP_SET_WAKE, | ||
| 685 | }; | ||
| 686 | |||
| 687 | static struct irq_chip sunxi_pinctrl_level_irq_chip = { | ||
| 688 | .irq_eoi = sunxi_pinctrl_irq_ack, | ||
| 645 | .irq_mask = sunxi_pinctrl_irq_mask, | 689 | .irq_mask = sunxi_pinctrl_irq_mask, |
| 646 | .irq_mask_ack = sunxi_pinctrl_irq_mask_ack, | ||
| 647 | .irq_unmask = sunxi_pinctrl_irq_unmask, | 690 | .irq_unmask = sunxi_pinctrl_irq_unmask, |
| 691 | /* Define irq_enable / disable to avoid spurious irqs for drivers | ||
| 692 | * using these to suppress irqs while they clear the irq source */ | ||
| 693 | .irq_enable = sunxi_pinctrl_irq_ack_unmask, | ||
| 694 | .irq_disable = sunxi_pinctrl_irq_mask, | ||
| 695 | .irq_request_resources = sunxi_pinctrl_irq_request_resources, | ||
| 696 | .irq_release_resources = sunxi_pinctrl_irq_release_resources, | ||
| 648 | .irq_set_type = sunxi_pinctrl_irq_set_type, | 697 | .irq_set_type = sunxi_pinctrl_irq_set_type, |
| 698 | .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_EOI_THREADED | | ||
| 699 | IRQCHIP_EOI_IF_HANDLED, | ||
| 649 | }; | 700 | }; |
| 650 | 701 | ||
| 651 | static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) | 702 | static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) |
| 652 | { | 703 | { |
| 653 | struct irq_chip *chip = irq_get_chip(irq); | 704 | struct irq_chip *chip = irq_get_chip(irq); |
| 654 | struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); | 705 | struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); |
| 655 | const unsigned long reg = readl(pctl->membase + IRQ_STATUS_REG); | 706 | unsigned long bank, reg, val; |
| 707 | |||
| 708 | for (bank = 0; bank < pctl->desc->irq_banks; bank++) | ||
| 709 | if (irq == pctl->irq[bank]) | ||
| 710 | break; | ||
| 711 | |||
| 712 | if (bank == pctl->desc->irq_banks) | ||
| 713 | return; | ||
| 656 | 714 | ||
| 657 | /* Clear all interrupts */ | 715 | reg = sunxi_irq_status_reg_from_bank(bank); |
| 658 | writel(reg, pctl->membase + IRQ_STATUS_REG); | 716 | val = readl(pctl->membase + reg); |
| 659 | 717 | ||
| 660 | if (reg) { | 718 | if (val) { |
| 661 | int irqoffset; | 719 | int irqoffset; |
| 662 | 720 | ||
| 663 | chained_irq_enter(chip, desc); | 721 | chained_irq_enter(chip, desc); |
| 664 | for_each_set_bit(irqoffset, ®, SUNXI_IRQ_NUMBER) { | 722 | for_each_set_bit(irqoffset, &val, IRQ_PER_BANK) { |
| 665 | int pin_irq = irq_find_mapping(pctl->domain, irqoffset); | 723 | int pin_irq = irq_find_mapping(pctl->domain, |
| 724 | bank * IRQ_PER_BANK + irqoffset); | ||
| 666 | generic_handle_irq(pin_irq); | 725 | generic_handle_irq(pin_irq); |
| 667 | } | 726 | } |
| 668 | chained_irq_exit(chip, desc); | 727 | chained_irq_exit(chip, desc); |
| @@ -730,8 +789,11 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) | |||
| 730 | 789 | ||
| 731 | while (func->name) { | 790 | while (func->name) { |
| 732 | /* Create interrupt mapping while we're at it */ | 791 | /* Create interrupt mapping while we're at it */ |
| 733 | if (!strcmp(func->name, "irq")) | 792 | if (!strcmp(func->name, "irq")) { |
| 734 | pctl->irq_array[func->irqnum] = pin->pin.number; | 793 | int irqnum = func->irqnum + func->irqbank * IRQ_PER_BANK; |
| 794 | pctl->irq_array[irqnum] = pin->pin.number; | ||
| 795 | } | ||
| 796 | |||
| 735 | sunxi_pinctrl_add_function(pctl, func->name); | 797 | sunxi_pinctrl_add_function(pctl, func->name); |
| 736 | func++; | 798 | func++; |
| 737 | } | 799 | } |
| @@ -801,6 +863,13 @@ int sunxi_pinctrl_init(struct platform_device *pdev, | |||
| 801 | pctl->dev = &pdev->dev; | 863 | pctl->dev = &pdev->dev; |
| 802 | pctl->desc = desc; | 864 | pctl->desc = desc; |
| 803 | 865 | ||
| 866 | pctl->irq_array = devm_kcalloc(&pdev->dev, | ||
| 867 | IRQ_PER_BANK * pctl->desc->irq_banks, | ||
| 868 | sizeof(*pctl->irq_array), | ||
| 869 | GFP_KERNEL); | ||
| 870 | if (!pctl->irq_array) | ||
| 871 | return -ENOMEM; | ||
| 872 | |||
| 804 | ret = sunxi_pinctrl_build_state(pdev); | 873 | ret = sunxi_pinctrl_build_state(pdev); |
| 805 | if (ret) { | 874 | if (ret) { |
| 806 | dev_err(&pdev->dev, "dt probe failed: %d\n", ret); | 875 | dev_err(&pdev->dev, "dt probe failed: %d\n", ret); |
| @@ -869,7 +938,7 @@ int sunxi_pinctrl_init(struct platform_device *pdev, | |||
| 869 | const struct sunxi_desc_pin *pin = pctl->desc->pins + i; | 938 | const struct sunxi_desc_pin *pin = pctl->desc->pins + i; |
| 870 | 939 | ||
| 871 | ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), | 940 | ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), |
| 872 | pin->pin.number, | 941 | pin->pin.number - pctl->desc->pin_base, |
| 873 | pin->pin.number, 1); | 942 | pin->pin.number, 1); |
| 874 | if (ret) | 943 | if (ret) |
| 875 | goto gpiochip_error; | 944 | goto gpiochip_error; |
| @@ -885,30 +954,51 @@ int sunxi_pinctrl_init(struct platform_device *pdev, | |||
| 885 | if (ret) | 954 | if (ret) |
| 886 | goto gpiochip_error; | 955 | goto gpiochip_error; |
| 887 | 956 | ||
| 888 | pctl->irq = irq_of_parse_and_map(node, 0); | 957 | pctl->irq = devm_kcalloc(&pdev->dev, |
| 958 | pctl->desc->irq_banks, | ||
| 959 | sizeof(*pctl->irq), | ||
| 960 | GFP_KERNEL); | ||
| 889 | if (!pctl->irq) { | 961 | if (!pctl->irq) { |
| 890 | ret = -EINVAL; | 962 | ret = -ENOMEM; |
| 891 | goto clk_error; | 963 | goto clk_error; |
| 892 | } | 964 | } |
| 893 | 965 | ||
| 894 | pctl->domain = irq_domain_add_linear(node, SUNXI_IRQ_NUMBER, | 966 | for (i = 0; i < pctl->desc->irq_banks; i++) { |
| 895 | &irq_domain_simple_ops, NULL); | 967 | pctl->irq[i] = platform_get_irq(pdev, i); |
| 968 | if (pctl->irq[i] < 0) { | ||
| 969 | ret = pctl->irq[i]; | ||
| 970 | goto clk_error; | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | pctl->domain = irq_domain_add_linear(node, | ||
| 975 | pctl->desc->irq_banks * IRQ_PER_BANK, | ||
| 976 | &irq_domain_simple_ops, | ||
| 977 | NULL); | ||
| 896 | if (!pctl->domain) { | 978 | if (!pctl->domain) { |
| 897 | dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); | 979 | dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); |
| 898 | ret = -ENOMEM; | 980 | ret = -ENOMEM; |
| 899 | goto clk_error; | 981 | goto clk_error; |
| 900 | } | 982 | } |
| 901 | 983 | ||
| 902 | for (i = 0; i < SUNXI_IRQ_NUMBER; i++) { | 984 | for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) { |
| 903 | int irqno = irq_create_mapping(pctl->domain, i); | 985 | int irqno = irq_create_mapping(pctl->domain, i); |
| 904 | 986 | ||
| 905 | irq_set_chip_and_handler(irqno, &sunxi_pinctrl_irq_chip, | 987 | irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip, |
| 906 | handle_simple_irq); | 988 | handle_edge_irq); |
| 907 | irq_set_chip_data(irqno, pctl); | 989 | irq_set_chip_data(irqno, pctl); |
| 908 | }; | 990 | }; |
| 909 | 991 | ||
| 910 | irq_set_chained_handler(pctl->irq, sunxi_pinctrl_irq_handler); | 992 | for (i = 0; i < pctl->desc->irq_banks; i++) { |
| 911 | irq_set_handler_data(pctl->irq, pctl); | 993 | /* Mask and clear all IRQs before registering a handler */ |
| 994 | writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i)); | ||
| 995 | writel(0xffffffff, | ||
| 996 | pctl->membase + sunxi_irq_status_reg_from_bank(i)); | ||
| 997 | |||
| 998 | irq_set_chained_handler(pctl->irq[i], | ||
| 999 | sunxi_pinctrl_irq_handler); | ||
| 1000 | irq_set_handler_data(pctl->irq[i], pctl); | ||
| 1001 | } | ||
| 912 | 1002 | ||
| 913 | dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); | 1003 | dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); |
| 914 | 1004 | ||
| @@ -917,8 +1007,7 @@ int sunxi_pinctrl_init(struct platform_device *pdev, | |||
| 917 | clk_error: | 1007 | clk_error: |
| 918 | clk_disable_unprepare(clk); | 1008 | clk_disable_unprepare(clk); |
| 919 | gpiochip_error: | 1009 | gpiochip_error: |
| 920 | if (gpiochip_remove(pctl->chip)) | 1010 | gpiochip_remove(pctl->chip); |
| 921 | dev_err(&pdev->dev, "failed to remove gpio chip\n"); | ||
| 922 | pinctrl_error: | 1011 | pinctrl_error: |
| 923 | pinctrl_unregister(pctl->pctl_dev); | 1012 | pinctrl_unregister(pctl->pctl_dev); |
| 924 | return ret; | 1013 | return ret; |
