18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/arm/plat-orion/gpio.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Marvell Orion SoC GPIO handling. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define DEBUG 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio.h> 228c2ecf20Sopenharmony_ci#include <linux/leds.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 258c2ecf20Sopenharmony_ci#include <linux/of_address.h> 268c2ecf20Sopenharmony_ci#include <plat/orion-gpio.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * GPIO unit register offsets. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci#define GPIO_OUT_OFF 0x0000 328c2ecf20Sopenharmony_ci#define GPIO_IO_CONF_OFF 0x0004 338c2ecf20Sopenharmony_ci#define GPIO_BLINK_EN_OFF 0x0008 348c2ecf20Sopenharmony_ci#define GPIO_IN_POL_OFF 0x000c 358c2ecf20Sopenharmony_ci#define GPIO_DATA_IN_OFF 0x0010 368c2ecf20Sopenharmony_ci#define GPIO_EDGE_CAUSE_OFF 0x0014 378c2ecf20Sopenharmony_ci#define GPIO_EDGE_MASK_OFF 0x0018 388c2ecf20Sopenharmony_ci#define GPIO_LEVEL_MASK_OFF 0x001c 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct orion_gpio_chip { 418c2ecf20Sopenharmony_ci struct gpio_chip chip; 428c2ecf20Sopenharmony_ci spinlock_t lock; 438c2ecf20Sopenharmony_ci void __iomem *base; 448c2ecf20Sopenharmony_ci unsigned long valid_input; 458c2ecf20Sopenharmony_ci unsigned long valid_output; 468c2ecf20Sopenharmony_ci int mask_offset; 478c2ecf20Sopenharmony_ci int secondary_irq_base; 488c2ecf20Sopenharmony_ci struct irq_domain *domain; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void __iomem *GPIO_OUT(struct orion_gpio_chip *ochip) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci return ochip->base + GPIO_OUT_OFF; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void __iomem *GPIO_IO_CONF(struct orion_gpio_chip *ochip) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return ochip->base + GPIO_IO_CONF_OFF; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void __iomem *GPIO_BLINK_EN(struct orion_gpio_chip *ochip) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return ochip->base + GPIO_BLINK_EN_OFF; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void __iomem *GPIO_IN_POL(struct orion_gpio_chip *ochip) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return ochip->base + GPIO_IN_POL_OFF; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void __iomem *GPIO_DATA_IN(struct orion_gpio_chip *ochip) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return ochip->base + GPIO_DATA_IN_OFF; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void __iomem *GPIO_EDGE_CAUSE(struct orion_gpio_chip *ochip) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return ochip->base + GPIO_EDGE_CAUSE_OFF; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void __iomem *GPIO_EDGE_MASK(struct orion_gpio_chip *ochip) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return ochip->base + ochip->mask_offset + GPIO_EDGE_MASK_OFF; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void __iomem *GPIO_LEVEL_MASK(struct orion_gpio_chip *ochip) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return ochip->base + ochip->mask_offset + GPIO_LEVEL_MASK_OFF; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct orion_gpio_chip orion_gpio_chips[2]; 938c2ecf20Sopenharmony_cistatic int orion_gpio_chip_count; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline void 968c2ecf20Sopenharmony_ci__set_direction(struct orion_gpio_chip *ochip, unsigned pin, int input) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci u32 u; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci u = readl(GPIO_IO_CONF(ochip)); 1018c2ecf20Sopenharmony_ci if (input) 1028c2ecf20Sopenharmony_ci u |= 1 << pin; 1038c2ecf20Sopenharmony_ci else 1048c2ecf20Sopenharmony_ci u &= ~(1 << pin); 1058c2ecf20Sopenharmony_ci writel(u, GPIO_IO_CONF(ochip)); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void __set_level(struct orion_gpio_chip *ochip, unsigned pin, int high) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u32 u; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci u = readl(GPIO_OUT(ochip)); 1138c2ecf20Sopenharmony_ci if (high) 1148c2ecf20Sopenharmony_ci u |= 1 << pin; 1158c2ecf20Sopenharmony_ci else 1168c2ecf20Sopenharmony_ci u &= ~(1 << pin); 1178c2ecf20Sopenharmony_ci writel(u, GPIO_OUT(ochip)); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline void 1218c2ecf20Sopenharmony_ci__set_blinking(struct orion_gpio_chip *ochip, unsigned pin, int blink) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u32 u; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci u = readl(GPIO_BLINK_EN(ochip)); 1268c2ecf20Sopenharmony_ci if (blink) 1278c2ecf20Sopenharmony_ci u |= 1 << pin; 1288c2ecf20Sopenharmony_ci else 1298c2ecf20Sopenharmony_ci u &= ~(1 << pin); 1308c2ecf20Sopenharmony_ci writel(u, GPIO_BLINK_EN(ochip)); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic inline int 1348c2ecf20Sopenharmony_ciorion_gpio_is_valid(struct orion_gpio_chip *ochip, unsigned pin, int mode) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci if (pin >= ochip->chip.ngpio) 1378c2ecf20Sopenharmony_ci goto err_out; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if ((mode & GPIO_INPUT_OK) && !test_bit(pin, &ochip->valid_input)) 1408c2ecf20Sopenharmony_ci goto err_out; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if ((mode & GPIO_OUTPUT_OK) && !test_bit(pin, &ochip->valid_output)) 1438c2ecf20Sopenharmony_ci goto err_out; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 1; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cierr_out: 1488c2ecf20Sopenharmony_ci pr_debug("%s: invalid GPIO %d\n", __func__, pin); 1498c2ecf20Sopenharmony_ci return false; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * GPIO primitives. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic int orion_gpio_request(struct gpio_chip *chip, unsigned pin) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (orion_gpio_is_valid(ochip, pin, GPIO_INPUT_OK) || 1608c2ecf20Sopenharmony_ci orion_gpio_is_valid(ochip, pin, GPIO_OUTPUT_OK)) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int orion_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 1698c2ecf20Sopenharmony_ci unsigned long flags; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!orion_gpio_is_valid(ochip, pin, GPIO_INPUT_OK)) 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spin_lock_irqsave(&ochip->lock, flags); 1758c2ecf20Sopenharmony_ci __set_direction(ochip, pin, 1); 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ochip->lock, flags); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int orion_gpio_get(struct gpio_chip *chip, unsigned pin) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 1848c2ecf20Sopenharmony_ci int val; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (readl(GPIO_IO_CONF(ochip)) & (1 << pin)) { 1878c2ecf20Sopenharmony_ci val = readl(GPIO_DATA_IN(ochip)) ^ readl(GPIO_IN_POL(ochip)); 1888c2ecf20Sopenharmony_ci } else { 1898c2ecf20Sopenharmony_ci val = readl(GPIO_OUT(ochip)); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return (val >> pin) & 1; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int 1968c2ecf20Sopenharmony_ciorion_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int value) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 1998c2ecf20Sopenharmony_ci unsigned long flags; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!orion_gpio_is_valid(ochip, pin, GPIO_OUTPUT_OK)) 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_lock_irqsave(&ochip->lock, flags); 2058c2ecf20Sopenharmony_ci __set_blinking(ochip, pin, 0); 2068c2ecf20Sopenharmony_ci __set_level(ochip, pin, value); 2078c2ecf20Sopenharmony_ci __set_direction(ochip, pin, 0); 2088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ochip->lock, flags); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void orion_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 2168c2ecf20Sopenharmony_ci unsigned long flags; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&ochip->lock, flags); 2198c2ecf20Sopenharmony_ci __set_level(ochip, pin, value); 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ochip->lock, flags); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int orion_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return irq_create_mapping(ochip->domain, 2288c2ecf20Sopenharmony_ci ochip->secondary_irq_base + pin); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * Orion-specific GPIO API extensions. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic struct orion_gpio_chip *orion_gpio_chip_find(int pin) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int i; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci for (i = 0; i < orion_gpio_chip_count; i++) { 2398c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = orion_gpio_chips + i; 2408c2ecf20Sopenharmony_ci struct gpio_chip *chip = &ochip->chip; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (pin >= chip->base && pin < chip->base + chip->ngpio) 2438c2ecf20Sopenharmony_ci return ochip; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return NULL; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_civoid __init orion_gpio_set_unused(unsigned pin) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = orion_gpio_chip_find(pin); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (ochip == NULL) 2548c2ecf20Sopenharmony_ci return; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pin -= ochip->chip.base; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Configure as output, drive low. */ 2598c2ecf20Sopenharmony_ci __set_level(ochip, pin, 0); 2608c2ecf20Sopenharmony_ci __set_direction(ochip, pin, 0); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_civoid __init orion_gpio_set_valid(unsigned pin, int mode) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = orion_gpio_chip_find(pin); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (ochip == NULL) 2688c2ecf20Sopenharmony_ci return; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pin -= ochip->chip.base; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (mode == 1) 2738c2ecf20Sopenharmony_ci mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (mode & GPIO_INPUT_OK) 2768c2ecf20Sopenharmony_ci __set_bit(pin, &ochip->valid_input); 2778c2ecf20Sopenharmony_ci else 2788c2ecf20Sopenharmony_ci __clear_bit(pin, &ochip->valid_input); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (mode & GPIO_OUTPUT_OK) 2818c2ecf20Sopenharmony_ci __set_bit(pin, &ochip->valid_output); 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci __clear_bit(pin, &ochip->valid_output); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid orion_gpio_set_blink(unsigned pin, int blink) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = orion_gpio_chip_find(pin); 2898c2ecf20Sopenharmony_ci unsigned long flags; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (ochip == NULL) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci spin_lock_irqsave(&ochip->lock, flags); 2958c2ecf20Sopenharmony_ci __set_level(ochip, pin & 31, 0); 2968c2ecf20Sopenharmony_ci __set_blinking(ochip, pin & 31, blink); 2978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ochip->lock, flags); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(orion_gpio_set_blink); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#define ORION_BLINK_HALF_PERIOD 100 /* ms */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciint orion_gpio_led_blink_set(struct gpio_desc *desc, int state, 3048c2ecf20Sopenharmony_ci unsigned long *delay_on, unsigned long *delay_off) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned gpio = desc_to_gpio(desc); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (delay_on && delay_off && !*delay_on && !*delay_off) 3098c2ecf20Sopenharmony_ci *delay_on = *delay_off = ORION_BLINK_HALF_PERIOD; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci switch (state) { 3128c2ecf20Sopenharmony_ci case GPIO_LED_NO_BLINK_LOW: 3138c2ecf20Sopenharmony_ci case GPIO_LED_NO_BLINK_HIGH: 3148c2ecf20Sopenharmony_ci orion_gpio_set_blink(gpio, 0); 3158c2ecf20Sopenharmony_ci gpio_set_value(gpio, state); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case GPIO_LED_BLINK: 3188c2ecf20Sopenharmony_ci orion_gpio_set_blink(gpio, 1); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(orion_gpio_led_blink_set); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/***************************************************************************** 3268c2ecf20Sopenharmony_ci * Orion GPIO IRQ 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same 3298c2ecf20Sopenharmony_ci * value of the line or the opposite value. 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * Level IRQ handlers: DATA_IN is used directly as cause register. 3328c2ecf20Sopenharmony_ci * Interrupt are masked by LEVEL_MASK registers. 3338c2ecf20Sopenharmony_ci * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. 3348c2ecf20Sopenharmony_ci * Interrupt are masked by EDGE_MASK registers. 3358c2ecf20Sopenharmony_ci * Both-edge handlers: Similar to regular Edge handlers, but also swaps 3368c2ecf20Sopenharmony_ci * the polarity to catch the next line transaction. 3378c2ecf20Sopenharmony_ci * This is a race condition that might not perfectly 3388c2ecf20Sopenharmony_ci * work on some use cases. 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Every eight GPIO lines are grouped (OR'ed) before going up to main 3418c2ecf20Sopenharmony_ci * cause register. 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * EDGE cause mask 3448c2ecf20Sopenharmony_ci * data-in /--------| |-----| |----\ 3458c2ecf20Sopenharmony_ci * -----| |----- ---- to main cause reg 3468c2ecf20Sopenharmony_ci * X \----------------| |----/ 3478c2ecf20Sopenharmony_ci * polarity LEVEL mask 3488c2ecf20Sopenharmony_ci * 3498c2ecf20Sopenharmony_ci ****************************************************************************/ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int gpio_irq_set_type(struct irq_data *d, u32 type) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3548c2ecf20Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 3558c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gc->private; 3568c2ecf20Sopenharmony_ci int pin; 3578c2ecf20Sopenharmony_ci u32 u; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci pin = d->hwirq - ochip->secondary_irq_base; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci u = readl(GPIO_IO_CONF(ochip)) & (1 << pin); 3628c2ecf20Sopenharmony_ci if (!u) { 3638c2ecf20Sopenharmony_ci return -EINVAL; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci type &= IRQ_TYPE_SENSE_MASK; 3678c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_NONE) 3688c2ecf20Sopenharmony_ci return -EINVAL; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Check if we need to change chip and handler */ 3718c2ecf20Sopenharmony_ci if (!(ct->type & type)) 3728c2ecf20Sopenharmony_ci if (irq_setup_alt_chip(d, type)) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * Configure interrupt polarity. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) { 3798c2ecf20Sopenharmony_ci u = readl(GPIO_IN_POL(ochip)); 3808c2ecf20Sopenharmony_ci u &= ~(1 << pin); 3818c2ecf20Sopenharmony_ci writel(u, GPIO_IN_POL(ochip)); 3828c2ecf20Sopenharmony_ci } else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) { 3838c2ecf20Sopenharmony_ci u = readl(GPIO_IN_POL(ochip)); 3848c2ecf20Sopenharmony_ci u |= 1 << pin; 3858c2ecf20Sopenharmony_ci writel(u, GPIO_IN_POL(ochip)); 3868c2ecf20Sopenharmony_ci } else if (type == IRQ_TYPE_EDGE_BOTH) { 3878c2ecf20Sopenharmony_ci u32 v; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci v = readl(GPIO_IN_POL(ochip)) ^ readl(GPIO_DATA_IN(ochip)); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * set initial polarity based on current input level 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci u = readl(GPIO_IN_POL(ochip)); 3958c2ecf20Sopenharmony_ci if (v & (1 << pin)) 3968c2ecf20Sopenharmony_ci u |= 1 << pin; /* falling */ 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci u &= ~(1 << pin); /* rising */ 3998c2ecf20Sopenharmony_ci writel(u, GPIO_IN_POL(ochip)); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void gpio_irq_handler(struct irq_desc *desc) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = irq_desc_get_handler_data(desc); 4078c2ecf20Sopenharmony_ci u32 cause, type; 4088c2ecf20Sopenharmony_ci int i; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (ochip == NULL) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci cause = readl(GPIO_DATA_IN(ochip)) & readl(GPIO_LEVEL_MASK(ochip)); 4148c2ecf20Sopenharmony_ci cause |= readl(GPIO_EDGE_CAUSE(ochip)) & readl(GPIO_EDGE_MASK(ochip)); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (i = 0; i < ochip->chip.ngpio; i++) { 4178c2ecf20Sopenharmony_ci int irq; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci irq = ochip->secondary_irq_base + i; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!(cause & (1 << i))) 4228c2ecf20Sopenharmony_ci continue; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci type = irq_get_trigger_type(irq); 4258c2ecf20Sopenharmony_ci if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { 4268c2ecf20Sopenharmony_ci /* Swap polarity (race with GPIO line) */ 4278c2ecf20Sopenharmony_ci u32 polarity; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci polarity = readl(GPIO_IN_POL(ochip)); 4308c2ecf20Sopenharmony_ci polarity ^= 1 << i; 4318c2ecf20Sopenharmony_ci writel(polarity, GPIO_IN_POL(ochip)); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci generic_handle_irq(irq); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 4388c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip = gpiochip_get_data(chip); 4448c2ecf20Sopenharmony_ci u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; 4458c2ecf20Sopenharmony_ci const char *label; 4468c2ecf20Sopenharmony_ci int i; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci out = readl_relaxed(GPIO_OUT(ochip)); 4498c2ecf20Sopenharmony_ci io_conf = readl_relaxed(GPIO_IO_CONF(ochip)); 4508c2ecf20Sopenharmony_ci blink = readl_relaxed(GPIO_BLINK_EN(ochip)); 4518c2ecf20Sopenharmony_ci in_pol = readl_relaxed(GPIO_IN_POL(ochip)); 4528c2ecf20Sopenharmony_ci data_in = readl_relaxed(GPIO_DATA_IN(ochip)); 4538c2ecf20Sopenharmony_ci cause = readl_relaxed(GPIO_EDGE_CAUSE(ochip)); 4548c2ecf20Sopenharmony_ci edg_msk = readl_relaxed(GPIO_EDGE_MASK(ochip)); 4558c2ecf20Sopenharmony_ci lvl_msk = readl_relaxed(GPIO_LEVEL_MASK(ochip)); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for_each_requested_gpio(chip, i, label) { 4588c2ecf20Sopenharmony_ci u32 msk; 4598c2ecf20Sopenharmony_ci bool is_out; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci msk = 1 << i; 4628c2ecf20Sopenharmony_ci is_out = !(io_conf & msk); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (is_out) { 4678c2ecf20Sopenharmony_ci seq_printf(s, " out %s %s\n", 4688c2ecf20Sopenharmony_ci out & msk ? "hi" : "lo", 4698c2ecf20Sopenharmony_ci blink & msk ? "(blink )" : ""); 4708c2ecf20Sopenharmony_ci continue; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci seq_printf(s, " in %s (act %s) - IRQ", 4748c2ecf20Sopenharmony_ci (data_in ^ in_pol) & msk ? "hi" : "lo", 4758c2ecf20Sopenharmony_ci in_pol & msk ? "lo" : "hi"); 4768c2ecf20Sopenharmony_ci if (!((edg_msk | lvl_msk) & msk)) { 4778c2ecf20Sopenharmony_ci seq_puts(s, " disabled\n"); 4788c2ecf20Sopenharmony_ci continue; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci if (edg_msk & msk) 4818c2ecf20Sopenharmony_ci seq_puts(s, " edge "); 4828c2ecf20Sopenharmony_ci if (lvl_msk & msk) 4838c2ecf20Sopenharmony_ci seq_puts(s, " level"); 4848c2ecf20Sopenharmony_ci seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear "); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci#else 4888c2ecf20Sopenharmony_ci#define orion_gpio_dbg_show NULL 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void orion_gpio_unmask_irq(struct irq_data *d) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 4948c2ecf20Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 4958c2ecf20Sopenharmony_ci u32 reg_val; 4968c2ecf20Sopenharmony_ci u32 mask = d->mask; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci irq_gc_lock(gc); 4998c2ecf20Sopenharmony_ci reg_val = irq_reg_readl(gc, ct->regs.mask); 5008c2ecf20Sopenharmony_ci reg_val |= mask; 5018c2ecf20Sopenharmony_ci irq_reg_writel(gc, reg_val, ct->regs.mask); 5028c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic void orion_gpio_mask_irq(struct irq_data *d) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 5088c2ecf20Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 5098c2ecf20Sopenharmony_ci u32 mask = d->mask; 5108c2ecf20Sopenharmony_ci u32 reg_val; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci irq_gc_lock(gc); 5138c2ecf20Sopenharmony_ci reg_val = irq_reg_readl(gc, ct->regs.mask); 5148c2ecf20Sopenharmony_ci reg_val &= ~mask; 5158c2ecf20Sopenharmony_ci irq_reg_writel(gc, reg_val, ct->regs.mask); 5168c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_civoid __init orion_gpio_init(struct device_node *np, 5208c2ecf20Sopenharmony_ci int gpio_base, int ngpio, 5218c2ecf20Sopenharmony_ci void __iomem *base, int mask_offset, 5228c2ecf20Sopenharmony_ci int secondary_irq_base, 5238c2ecf20Sopenharmony_ci int irqs[4]) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct orion_gpio_chip *ochip; 5268c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 5278c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 5288c2ecf20Sopenharmony_ci char gc_label[16]; 5298c2ecf20Sopenharmony_ci int i; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (orion_gpio_chip_count == ARRAY_SIZE(orion_gpio_chips)) 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci snprintf(gc_label, sizeof(gc_label), "orion_gpio%d", 5358c2ecf20Sopenharmony_ci orion_gpio_chip_count); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ochip = orion_gpio_chips + orion_gpio_chip_count; 5388c2ecf20Sopenharmony_ci ochip->chip.label = kstrdup(gc_label, GFP_KERNEL); 5398c2ecf20Sopenharmony_ci ochip->chip.request = orion_gpio_request; 5408c2ecf20Sopenharmony_ci ochip->chip.direction_input = orion_gpio_direction_input; 5418c2ecf20Sopenharmony_ci ochip->chip.get = orion_gpio_get; 5428c2ecf20Sopenharmony_ci ochip->chip.direction_output = orion_gpio_direction_output; 5438c2ecf20Sopenharmony_ci ochip->chip.set = orion_gpio_set; 5448c2ecf20Sopenharmony_ci ochip->chip.to_irq = orion_gpio_to_irq; 5458c2ecf20Sopenharmony_ci ochip->chip.base = gpio_base; 5468c2ecf20Sopenharmony_ci ochip->chip.ngpio = ngpio; 5478c2ecf20Sopenharmony_ci ochip->chip.can_sleep = 0; 5488c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5498c2ecf20Sopenharmony_ci ochip->chip.of_node = np; 5508c2ecf20Sopenharmony_ci#endif 5518c2ecf20Sopenharmony_ci ochip->chip.dbg_show = orion_gpio_dbg_show; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci spin_lock_init(&ochip->lock); 5548c2ecf20Sopenharmony_ci ochip->base = (void __iomem *)base; 5558c2ecf20Sopenharmony_ci ochip->valid_input = 0; 5568c2ecf20Sopenharmony_ci ochip->valid_output = 0; 5578c2ecf20Sopenharmony_ci ochip->mask_offset = mask_offset; 5588c2ecf20Sopenharmony_ci ochip->secondary_irq_base = secondary_irq_base; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci gpiochip_add_data(&ochip->chip, ochip); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * Mask and clear GPIO interrupts. 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci writel(0, GPIO_EDGE_CAUSE(ochip)); 5668c2ecf20Sopenharmony_ci writel(0, GPIO_EDGE_MASK(ochip)); 5678c2ecf20Sopenharmony_ci writel(0, GPIO_LEVEL_MASK(ochip)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Setup the interrupt handlers. Each chip can have up to 4 5708c2ecf20Sopenharmony_ci * interrupt handlers, with each handler dealing with 8 GPIO 5718c2ecf20Sopenharmony_ci * pins. */ 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 5748c2ecf20Sopenharmony_ci if (irqs[i]) { 5758c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irqs[i], 5768c2ecf20Sopenharmony_ci gpio_irq_handler, 5778c2ecf20Sopenharmony_ci ochip); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci gc = irq_alloc_generic_chip("orion_gpio_irq", 2, 5828c2ecf20Sopenharmony_ci secondary_irq_base, 5838c2ecf20Sopenharmony_ci ochip->base, handle_level_irq); 5848c2ecf20Sopenharmony_ci gc->private = ochip; 5858c2ecf20Sopenharmony_ci ct = gc->chip_types; 5868c2ecf20Sopenharmony_ci ct->regs.mask = ochip->mask_offset + GPIO_LEVEL_MASK_OFF; 5878c2ecf20Sopenharmony_ci ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; 5888c2ecf20Sopenharmony_ci ct->chip.irq_mask = orion_gpio_mask_irq; 5898c2ecf20Sopenharmony_ci ct->chip.irq_unmask = orion_gpio_unmask_irq; 5908c2ecf20Sopenharmony_ci ct->chip.irq_set_type = gpio_irq_set_type; 5918c2ecf20Sopenharmony_ci ct->chip.name = ochip->chip.label; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ct++; 5948c2ecf20Sopenharmony_ci ct->regs.mask = ochip->mask_offset + GPIO_EDGE_MASK_OFF; 5958c2ecf20Sopenharmony_ci ct->regs.ack = GPIO_EDGE_CAUSE_OFF; 5968c2ecf20Sopenharmony_ci ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; 5978c2ecf20Sopenharmony_ci ct->chip.irq_ack = irq_gc_ack_clr_bit; 5988c2ecf20Sopenharmony_ci ct->chip.irq_mask = orion_gpio_mask_irq; 5998c2ecf20Sopenharmony_ci ct->chip.irq_unmask = orion_gpio_unmask_irq; 6008c2ecf20Sopenharmony_ci ct->chip.irq_set_type = gpio_irq_set_type; 6018c2ecf20Sopenharmony_ci ct->handler = handle_edge_irq; 6028c2ecf20Sopenharmony_ci ct->chip.name = ochip->chip.label; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE, 6058c2ecf20Sopenharmony_ci IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Setup irq domain on top of the generic chip. */ 6088c2ecf20Sopenharmony_ci ochip->domain = irq_domain_add_legacy(np, 6098c2ecf20Sopenharmony_ci ochip->chip.ngpio, 6108c2ecf20Sopenharmony_ci ochip->secondary_irq_base, 6118c2ecf20Sopenharmony_ci ochip->secondary_irq_base, 6128c2ecf20Sopenharmony_ci &irq_domain_simple_ops, 6138c2ecf20Sopenharmony_ci ochip); 6148c2ecf20Sopenharmony_ci if (!ochip->domain) 6158c2ecf20Sopenharmony_ci panic("%s: couldn't allocate irq domain (DT).\n", 6168c2ecf20Sopenharmony_ci ochip->chip.label); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci orion_gpio_chip_count++; 6198c2ecf20Sopenharmony_ci} 620