| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Driver for the Atmel PIO4 controller |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2015 Atmel, |
|---|
| 5 | 6 | * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This software is licensed under the terms of the GNU General Public |
|---|
| 8 | | - * License version 2, as published by the Free Software Foundation, and |
|---|
| 9 | | - * may be copied, distributed, and modified under those terms. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | | - * GNU General Public License for more details. |
|---|
| 15 | 7 | */ |
|---|
| 16 | 8 | |
|---|
| 17 | 9 | #include <dt-bindings/pinctrl/at91.h> |
|---|
| 18 | 10 | #include <linux/clk.h> |
|---|
| 19 | 11 | #include <linux/gpio/driver.h> |
|---|
| 20 | | -/* FIXME: needed for gpio_to_irq(), get rid of this */ |
|---|
| 21 | | -#include <linux/gpio.h> |
|---|
| 22 | 12 | #include <linux/interrupt.h> |
|---|
| 23 | 13 | #include <linux/io.h> |
|---|
| 24 | 14 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 116 | 106 | * @irq_domain: irq domain for the gpio controller. |
|---|
| 117 | 107 | * @irqs: table containing the hw irq number of the bank. The index of the |
|---|
| 118 | 108 | * table is the bank id. |
|---|
| 109 | + * @pm_wakeup_sources: bitmap of wakeup sources (lines) |
|---|
| 110 | + * @pm_suspend_backup: backup/restore register values on suspend/resume |
|---|
| 119 | 111 | * @dev: device entry for the Atmel PIO controller. |
|---|
| 120 | 112 | * @node: node of the Atmel PIO controller. |
|---|
| 121 | 113 | */ |
|---|
| .. | .. |
|---|
| 264 | 256 | .irq_set_wake = atmel_gpio_irq_set_wake, |
|---|
| 265 | 257 | }; |
|---|
| 266 | 258 | |
|---|
| 259 | +static int atmel_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
|---|
| 260 | +{ |
|---|
| 261 | + struct atmel_pioctrl *atmel_pioctrl = gpiochip_get_data(chip); |
|---|
| 262 | + |
|---|
| 263 | + return irq_find_mapping(atmel_pioctrl->irq_domain, offset); |
|---|
| 264 | +} |
|---|
| 265 | + |
|---|
| 267 | 266 | static void atmel_gpio_irq_handler(struct irq_desc *desc) |
|---|
| 268 | 267 | { |
|---|
| 269 | 268 | unsigned int irq = irq_desc_get_irq(desc); |
|---|
| .. | .. |
|---|
| 297 | 296 | break; |
|---|
| 298 | 297 | |
|---|
| 299 | 298 | for_each_set_bit(n, &isr, BITS_PER_LONG) |
|---|
| 300 | | - generic_handle_irq(gpio_to_irq(bank * |
|---|
| 301 | | - ATMEL_PIO_NPINS_PER_BANK + n)); |
|---|
| 299 | + generic_handle_irq(atmel_gpio_to_irq( |
|---|
| 300 | + atmel_pioctrl->gpio_chip, |
|---|
| 301 | + bank * ATMEL_PIO_NPINS_PER_BANK + n)); |
|---|
| 302 | 302 | } |
|---|
| 303 | 303 | |
|---|
| 304 | 304 | chained_irq_exit(chip, desc); |
|---|
| .. | .. |
|---|
| 328 | 328 | reg = atmel_gpio_read(atmel_pioctrl, pin->bank, ATMEL_PIO_PDSR); |
|---|
| 329 | 329 | |
|---|
| 330 | 330 | return !!(reg & BIT(pin->line)); |
|---|
| 331 | +} |
|---|
| 332 | + |
|---|
| 333 | +static int atmel_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, |
|---|
| 334 | + unsigned long *bits) |
|---|
| 335 | +{ |
|---|
| 336 | + struct atmel_pioctrl *atmel_pioctrl = gpiochip_get_data(chip); |
|---|
| 337 | + unsigned int bank; |
|---|
| 338 | + |
|---|
| 339 | + bitmap_zero(bits, atmel_pioctrl->npins); |
|---|
| 340 | + |
|---|
| 341 | + for (bank = 0; bank < atmel_pioctrl->nbanks; bank++) { |
|---|
| 342 | + unsigned int word = bank; |
|---|
| 343 | + unsigned int offset = 0; |
|---|
| 344 | + unsigned int reg; |
|---|
| 345 | + |
|---|
| 346 | +#if ATMEL_PIO_NPINS_PER_BANK != BITS_PER_LONG |
|---|
| 347 | + word = BIT_WORD(bank * ATMEL_PIO_NPINS_PER_BANK); |
|---|
| 348 | + offset = bank * ATMEL_PIO_NPINS_PER_BANK % BITS_PER_LONG; |
|---|
| 349 | +#endif |
|---|
| 350 | + if (!mask[word]) |
|---|
| 351 | + continue; |
|---|
| 352 | + |
|---|
| 353 | + reg = atmel_gpio_read(atmel_pioctrl, bank, ATMEL_PIO_PDSR); |
|---|
| 354 | + bits[word] |= mask[word] & (reg << offset); |
|---|
| 355 | + } |
|---|
| 356 | + |
|---|
| 357 | + return 0; |
|---|
| 331 | 358 | } |
|---|
| 332 | 359 | |
|---|
| 333 | 360 | static int atmel_gpio_direction_output(struct gpio_chip *chip, unsigned offset, |
|---|
| .. | .. |
|---|
| 360 | 387 | BIT(pin->line)); |
|---|
| 361 | 388 | } |
|---|
| 362 | 389 | |
|---|
| 363 | | -static int atmel_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
|---|
| 390 | +static void atmel_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, |
|---|
| 391 | + unsigned long *bits) |
|---|
| 364 | 392 | { |
|---|
| 365 | 393 | struct atmel_pioctrl *atmel_pioctrl = gpiochip_get_data(chip); |
|---|
| 394 | + unsigned int bank; |
|---|
| 366 | 395 | |
|---|
| 367 | | - return irq_find_mapping(atmel_pioctrl->irq_domain, offset); |
|---|
| 396 | + for (bank = 0; bank < atmel_pioctrl->nbanks; bank++) { |
|---|
| 397 | + unsigned int bitmask; |
|---|
| 398 | + unsigned int word = bank; |
|---|
| 399 | + |
|---|
| 400 | +/* |
|---|
| 401 | + * On a 64-bit platform, BITS_PER_LONG is 64 so it is necessary to iterate over |
|---|
| 402 | + * two 32bit words to handle the whole bitmask |
|---|
| 403 | + */ |
|---|
| 404 | +#if ATMEL_PIO_NPINS_PER_BANK != BITS_PER_LONG |
|---|
| 405 | + word = BIT_WORD(bank * ATMEL_PIO_NPINS_PER_BANK); |
|---|
| 406 | +#endif |
|---|
| 407 | + if (!mask[word]) |
|---|
| 408 | + continue; |
|---|
| 409 | + |
|---|
| 410 | + bitmask = mask[word] & bits[word]; |
|---|
| 411 | + atmel_gpio_write(atmel_pioctrl, bank, ATMEL_PIO_SODR, bitmask); |
|---|
| 412 | + |
|---|
| 413 | + bitmask = mask[word] & ~bits[word]; |
|---|
| 414 | + atmel_gpio_write(atmel_pioctrl, bank, ATMEL_PIO_CODR, bitmask); |
|---|
| 415 | + |
|---|
| 416 | +#if ATMEL_PIO_NPINS_PER_BANK != BITS_PER_LONG |
|---|
| 417 | + mask[word] >>= ATMEL_PIO_NPINS_PER_BANK; |
|---|
| 418 | + bits[word] >>= ATMEL_PIO_NPINS_PER_BANK; |
|---|
| 419 | +#endif |
|---|
| 420 | + } |
|---|
| 368 | 421 | } |
|---|
| 369 | 422 | |
|---|
| 370 | 423 | static struct gpio_chip atmel_gpio_chip = { |
|---|
| 371 | 424 | .direction_input = atmel_gpio_direction_input, |
|---|
| 372 | 425 | .get = atmel_gpio_get, |
|---|
| 426 | + .get_multiple = atmel_gpio_get_multiple, |
|---|
| 373 | 427 | .direction_output = atmel_gpio_direction_output, |
|---|
| 374 | 428 | .set = atmel_gpio_set, |
|---|
| 429 | + .set_multiple = atmel_gpio_set_multiple, |
|---|
| 375 | 430 | .to_irq = atmel_gpio_to_irq, |
|---|
| 376 | 431 | .base = 0, |
|---|
| 377 | 432 | }; |
|---|
| .. | .. |
|---|
| 869 | 924 | |
|---|
| 870 | 925 | static int __maybe_unused atmel_pctrl_suspend(struct device *dev) |
|---|
| 871 | 926 | { |
|---|
| 872 | | - struct platform_device *pdev = to_platform_device(dev); |
|---|
| 873 | | - struct atmel_pioctrl *atmel_pioctrl = platform_get_drvdata(pdev); |
|---|
| 927 | + struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(dev); |
|---|
| 874 | 928 | int i, j; |
|---|
| 875 | 929 | |
|---|
| 876 | 930 | /* |
|---|
| .. | .. |
|---|
| 898 | 952 | |
|---|
| 899 | 953 | static int __maybe_unused atmel_pctrl_resume(struct device *dev) |
|---|
| 900 | 954 | { |
|---|
| 901 | | - struct platform_device *pdev = to_platform_device(dev); |
|---|
| 902 | | - struct atmel_pioctrl *atmel_pioctrl = platform_get_drvdata(pdev); |
|---|
| 955 | + struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(dev); |
|---|
| 903 | 956 | int i, j; |
|---|
| 904 | 957 | |
|---|
| 905 | 958 | for (i = 0; i < atmel_pioctrl->nbanks; i++) { |
|---|
| .. | .. |
|---|
| 930 | 983 | .nbanks = 4, |
|---|
| 931 | 984 | }; |
|---|
| 932 | 985 | |
|---|
| 986 | +static const struct atmel_pioctrl_data microchip_sama7g5_pioctrl_data = { |
|---|
| 987 | + .nbanks = 5, |
|---|
| 988 | +}; |
|---|
| 989 | + |
|---|
| 933 | 990 | static const struct of_device_id atmel_pctrl_of_match[] = { |
|---|
| 934 | 991 | { |
|---|
| 935 | 992 | .compatible = "atmel,sama5d2-pinctrl", |
|---|
| 936 | 993 | .data = &atmel_sama5d2_pioctrl_data, |
|---|
| 994 | + }, { |
|---|
| 995 | + .compatible = "microchip,sama7g5-pinctrl", |
|---|
| 996 | + .data = µchip_sama7g5_pioctrl_data, |
|---|
| 937 | 997 | }, { |
|---|
| 938 | 998 | /* sentinel */ |
|---|
| 939 | 999 | } |
|---|
| .. | .. |
|---|
| 966 | 1026 | atmel_pioctrl->nbanks = atmel_pioctrl_data->nbanks; |
|---|
| 967 | 1027 | atmel_pioctrl->npins = atmel_pioctrl->nbanks * ATMEL_PIO_NPINS_PER_BANK; |
|---|
| 968 | 1028 | |
|---|
| 969 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 970 | | - atmel_pioctrl->reg_base = devm_ioremap_resource(dev, res); |
|---|
| 1029 | + atmel_pioctrl->reg_base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 971 | 1030 | if (IS_ERR(atmel_pioctrl->reg_base)) |
|---|
| 972 | | - return -EINVAL; |
|---|
| 1031 | + return PTR_ERR(atmel_pioctrl->reg_base); |
|---|
| 973 | 1032 | |
|---|
| 974 | 1033 | atmel_pioctrl->clk = devm_clk_get(dev, NULL); |
|---|
| 975 | 1034 | if (IS_ERR(atmel_pioctrl->clk)) { |
|---|
| .. | .. |
|---|
| 1022 | 1081 | |
|---|
| 1023 | 1082 | pin_desc[i].number = i; |
|---|
| 1024 | 1083 | /* Pin naming convention: P(bank_name)(bank_pin_number). */ |
|---|
| 1025 | | - pin_desc[i].name = kasprintf(GFP_KERNEL, "P%c%d", |
|---|
| 1026 | | - bank + 'A', line); |
|---|
| 1084 | + pin_desc[i].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "P%c%d", |
|---|
| 1085 | + bank + 'A', line); |
|---|
| 1086 | + if (!pin_desc[i].name) |
|---|
| 1087 | + return -ENOMEM; |
|---|
| 1027 | 1088 | |
|---|
| 1028 | 1089 | group->name = group_names[i] = pin_desc[i].name; |
|---|
| 1029 | 1090 | group->pin = pin_desc[i].number; |
|---|
| .. | .. |
|---|
| 1080 | 1141 | dev_err(dev, "can't add the irq domain\n"); |
|---|
| 1081 | 1142 | return -ENODEV; |
|---|
| 1082 | 1143 | } |
|---|
| 1083 | | - atmel_pioctrl->irq_domain->name = "atmel gpio"; |
|---|
| 1084 | 1144 | |
|---|
| 1085 | 1145 | for (i = 0; i < atmel_pioctrl->npins; i++) { |
|---|
| 1086 | 1146 | int irq = irq_create_mapping(atmel_pioctrl->irq_domain, i); |
|---|