13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Copyright (c) 2013 MundoReader S.L. 43d0407baSopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de> 53d0407baSopenharmony_ci * 63d0407baSopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co. Ltd. 73d0407baSopenharmony_ci */ 83d0407baSopenharmony_ci 93d0407baSopenharmony_ci#include <linux/bitops.h> 103d0407baSopenharmony_ci#include <linux/clk.h> 113d0407baSopenharmony_ci#include <linux/device.h> 123d0407baSopenharmony_ci#include <linux/err.h> 133d0407baSopenharmony_ci#include <linux/gpio/driver.h> 143d0407baSopenharmony_ci#include <linux/init.h> 153d0407baSopenharmony_ci#include <linux/interrupt.h> 163d0407baSopenharmony_ci#include <linux/io.h> 173d0407baSopenharmony_ci#include <linux/module.h> 183d0407baSopenharmony_ci#include <linux/of.h> 193d0407baSopenharmony_ci#include <linux/of_address.h> 203d0407baSopenharmony_ci#include <linux/of_device.h> 213d0407baSopenharmony_ci#include <linux/of_irq.h> 223d0407baSopenharmony_ci#include <linux/regmap.h> 233d0407baSopenharmony_ci 243d0407baSopenharmony_ci#include "../pinctrl/core.h" 253d0407baSopenharmony_ci#include "../pinctrl/pinctrl-rockchip.h" 263d0407baSopenharmony_ci 273d0407baSopenharmony_ci#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ 283d0407baSopenharmony_ci#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ 293d0407baSopenharmony_ci#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ 303d0407baSopenharmony_ci 313d0407baSopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v1 = { 323d0407baSopenharmony_ci .port_dr = 0x00, 333d0407baSopenharmony_ci .port_ddr = 0x04, 343d0407baSopenharmony_ci .int_en = 0x30, 353d0407baSopenharmony_ci .int_mask = 0x34, 363d0407baSopenharmony_ci .int_type = 0x38, 373d0407baSopenharmony_ci .int_polarity = 0x3c, 383d0407baSopenharmony_ci .int_status = 0x40, 393d0407baSopenharmony_ci .int_rawstatus = 0x44, 403d0407baSopenharmony_ci .debounce = 0x48, 413d0407baSopenharmony_ci .port_eoi = 0x4c, 423d0407baSopenharmony_ci .ext_port = 0x50, 433d0407baSopenharmony_ci}; 443d0407baSopenharmony_ci 453d0407baSopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v2 = { 463d0407baSopenharmony_ci .port_dr = 0x00, 473d0407baSopenharmony_ci .port_ddr = 0x08, 483d0407baSopenharmony_ci .int_en = 0x10, 493d0407baSopenharmony_ci .int_mask = 0x18, 503d0407baSopenharmony_ci .int_type = 0x20, 513d0407baSopenharmony_ci .int_polarity = 0x28, 523d0407baSopenharmony_ci .int_bothedge = 0x30, 533d0407baSopenharmony_ci .int_status = 0x50, 543d0407baSopenharmony_ci .int_rawstatus = 0x58, 553d0407baSopenharmony_ci .debounce = 0x38, 563d0407baSopenharmony_ci .dbclk_div_en = 0x40, 573d0407baSopenharmony_ci .dbclk_div_con = 0x48, 583d0407baSopenharmony_ci .port_eoi = 0x60, 593d0407baSopenharmony_ci .ext_port = 0x70, 603d0407baSopenharmony_ci .version_id = 0x78, 613d0407baSopenharmony_ci}; 623d0407baSopenharmony_ci 633d0407baSopenharmony_cistatic inline void gpio_writel_v2(u32 val, void __iomem *reg) 643d0407baSopenharmony_ci{ 653d0407baSopenharmony_ci writel((val & 0xffff) | 0xffff0000, reg); 663d0407baSopenharmony_ci writel((val >> 16) | 0xffff0000, reg + 0x4); 673d0407baSopenharmony_ci} 683d0407baSopenharmony_ci 693d0407baSopenharmony_cistatic inline u32 gpio_readl_v2(void __iomem *reg) 703d0407baSopenharmony_ci{ 713d0407baSopenharmony_ci return readl(reg + 0x4) << 16 | readl(reg); 723d0407baSopenharmony_ci} 733d0407baSopenharmony_ci 743d0407baSopenharmony_cistatic inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank, 753d0407baSopenharmony_ci u32 value, unsigned int offset) 763d0407baSopenharmony_ci{ 773d0407baSopenharmony_ci void __iomem *reg = bank->reg_base + offset; 783d0407baSopenharmony_ci 793d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) 803d0407baSopenharmony_ci gpio_writel_v2(value, reg); 813d0407baSopenharmony_ci else 823d0407baSopenharmony_ci writel(value, reg); 833d0407baSopenharmony_ci} 843d0407baSopenharmony_ci 853d0407baSopenharmony_cistatic inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank, 863d0407baSopenharmony_ci unsigned int offset) 873d0407baSopenharmony_ci{ 883d0407baSopenharmony_ci void __iomem *reg = bank->reg_base + offset; 893d0407baSopenharmony_ci u32 value; 903d0407baSopenharmony_ci 913d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) 923d0407baSopenharmony_ci value = gpio_readl_v2(reg); 933d0407baSopenharmony_ci else 943d0407baSopenharmony_ci value = readl(reg); 953d0407baSopenharmony_ci 963d0407baSopenharmony_ci return value; 973d0407baSopenharmony_ci} 983d0407baSopenharmony_ci 993d0407baSopenharmony_cistatic inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank, 1003d0407baSopenharmony_ci u32 bit, u32 value, 1013d0407baSopenharmony_ci unsigned int offset) 1023d0407baSopenharmony_ci{ 1033d0407baSopenharmony_ci void __iomem *reg = bank->reg_base + offset; 1043d0407baSopenharmony_ci u32 data; 1053d0407baSopenharmony_ci 1063d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 1073d0407baSopenharmony_ci if (value) 1083d0407baSopenharmony_ci data = BIT(bit % 16) | BIT(bit % 16 + 16); 1093d0407baSopenharmony_ci else 1103d0407baSopenharmony_ci data = BIT(bit % 16 + 16); 1113d0407baSopenharmony_ci writel(data, bit >= 16 ? reg + 0x4 : reg); 1123d0407baSopenharmony_ci } else { 1133d0407baSopenharmony_ci data = readl(reg); 1143d0407baSopenharmony_ci data &= ~BIT(bit); 1153d0407baSopenharmony_ci if (value) 1163d0407baSopenharmony_ci data |= BIT(bit); 1173d0407baSopenharmony_ci writel(data, reg); 1183d0407baSopenharmony_ci } 1193d0407baSopenharmony_ci} 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_cistatic inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank, 1223d0407baSopenharmony_ci u32 bit, unsigned int offset) 1233d0407baSopenharmony_ci{ 1243d0407baSopenharmony_ci void __iomem *reg = bank->reg_base + offset; 1253d0407baSopenharmony_ci u32 data; 1263d0407baSopenharmony_ci 1273d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 1283d0407baSopenharmony_ci data = readl(bit >= 16 ? reg + 0x4 : reg); 1293d0407baSopenharmony_ci data >>= bit % 16; 1303d0407baSopenharmony_ci } else { 1313d0407baSopenharmony_ci data = readl(reg); 1323d0407baSopenharmony_ci data >>= bit; 1333d0407baSopenharmony_ci } 1343d0407baSopenharmony_ci 1353d0407baSopenharmony_ci return data & (0x1); 1363d0407baSopenharmony_ci} 1373d0407baSopenharmony_ci 1383d0407baSopenharmony_cistatic int rockchip_gpio_get_direction(struct gpio_chip *chip, 1393d0407baSopenharmony_ci unsigned int offset) 1403d0407baSopenharmony_ci{ 1413d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(chip); 1423d0407baSopenharmony_ci u32 data; 1433d0407baSopenharmony_ci 1443d0407baSopenharmony_ci data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr); 1453d0407baSopenharmony_ci if (data) 1463d0407baSopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 1473d0407baSopenharmony_ci 1483d0407baSopenharmony_ci return GPIO_LINE_DIRECTION_IN; 1493d0407baSopenharmony_ci} 1503d0407baSopenharmony_ci 1513d0407baSopenharmony_cistatic int rockchip_gpio_set_direction(struct gpio_chip *chip, 1523d0407baSopenharmony_ci unsigned int offset, bool input) 1533d0407baSopenharmony_ci{ 1543d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(chip); 1553d0407baSopenharmony_ci unsigned long flags; 1563d0407baSopenharmony_ci u32 data = input ? 0 : 1; 1573d0407baSopenharmony_ci 1583d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 1593d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr); 1603d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 1613d0407baSopenharmony_ci 1623d0407baSopenharmony_ci return 0; 1633d0407baSopenharmony_ci} 1643d0407baSopenharmony_ci 1653d0407baSopenharmony_cistatic void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, 1663d0407baSopenharmony_ci int value) 1673d0407baSopenharmony_ci{ 1683d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 1693d0407baSopenharmony_ci unsigned long flags; 1703d0407baSopenharmony_ci 1713d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 1723d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr); 1733d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 1743d0407baSopenharmony_ci} 1753d0407baSopenharmony_ci 1763d0407baSopenharmony_cistatic int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset) 1773d0407baSopenharmony_ci{ 1783d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 1793d0407baSopenharmony_ci u32 data; 1803d0407baSopenharmony_ci 1813d0407baSopenharmony_ci data = readl(bank->reg_base + bank->gpio_regs->ext_port); 1823d0407baSopenharmony_ci data >>= offset; 1833d0407baSopenharmony_ci data &= 1; 1843d0407baSopenharmony_ci 1853d0407baSopenharmony_ci return data; 1863d0407baSopenharmony_ci} 1873d0407baSopenharmony_ci 1883d0407baSopenharmony_cistatic int rockchip_gpio_set_debounce(struct gpio_chip *gc, 1893d0407baSopenharmony_ci unsigned int offset, 1903d0407baSopenharmony_ci unsigned int debounce) 1913d0407baSopenharmony_ci{ 1923d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 1933d0407baSopenharmony_ci const struct rockchip_gpio_regs *reg = bank->gpio_regs; 1943d0407baSopenharmony_ci unsigned long flags, div_reg, freq, max_debounce; 1953d0407baSopenharmony_ci bool div_debounce_support; 1963d0407baSopenharmony_ci unsigned int cur_div_reg; 1973d0407baSopenharmony_ci u64 div; 1983d0407baSopenharmony_ci 1993d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2 && !IS_ERR(bank->db_clk)) { 2003d0407baSopenharmony_ci div_debounce_support = true; 2013d0407baSopenharmony_ci freq = clk_get_rate(bank->db_clk); 2023d0407baSopenharmony_ci max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq; 2033d0407baSopenharmony_ci if (debounce > max_debounce) 2043d0407baSopenharmony_ci return -EINVAL; 2053d0407baSopenharmony_ci 2063d0407baSopenharmony_ci div = debounce * freq; 2073d0407baSopenharmony_ci div_reg = DIV_ROUND_CLOSEST_ULL(div, 2 * USEC_PER_SEC) - 1; 2083d0407baSopenharmony_ci } else { 2093d0407baSopenharmony_ci div_debounce_support = false; 2103d0407baSopenharmony_ci } 2113d0407baSopenharmony_ci 2123d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 2133d0407baSopenharmony_ci 2143d0407baSopenharmony_ci /* Only the v1 needs to configure div_en and div_con for dbclk */ 2153d0407baSopenharmony_ci if (debounce) { 2163d0407baSopenharmony_ci if (div_debounce_support) { 2173d0407baSopenharmony_ci /* Configure the max debounce from consumers */ 2183d0407baSopenharmony_ci cur_div_reg = readl(bank->reg_base + 2193d0407baSopenharmony_ci reg->dbclk_div_con); 2203d0407baSopenharmony_ci if (cur_div_reg < div_reg) 2213d0407baSopenharmony_ci writel(div_reg, bank->reg_base + 2223d0407baSopenharmony_ci reg->dbclk_div_con); 2233d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 1, 2243d0407baSopenharmony_ci reg->dbclk_div_en); 2253d0407baSopenharmony_ci } 2263d0407baSopenharmony_ci 2273d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 1, reg->debounce); 2283d0407baSopenharmony_ci } else { 2293d0407baSopenharmony_ci if (div_debounce_support) 2303d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 0, 2313d0407baSopenharmony_ci reg->dbclk_div_en); 2323d0407baSopenharmony_ci 2333d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 0, reg->debounce); 2343d0407baSopenharmony_ci } 2353d0407baSopenharmony_ci 2363d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 2373d0407baSopenharmony_ci 2383d0407baSopenharmony_ci /* Enable or disable dbclk at last */ 2393d0407baSopenharmony_ci if (div_debounce_support) { 2403d0407baSopenharmony_ci if (debounce) 2413d0407baSopenharmony_ci clk_prepare_enable(bank->db_clk); 2423d0407baSopenharmony_ci else 2433d0407baSopenharmony_ci clk_disable_unprepare(bank->db_clk); 2443d0407baSopenharmony_ci } 2453d0407baSopenharmony_ci 2463d0407baSopenharmony_ci return 0; 2473d0407baSopenharmony_ci} 2483d0407baSopenharmony_ci 2493d0407baSopenharmony_cistatic int rockchip_gpio_direction_input(struct gpio_chip *gc, 2503d0407baSopenharmony_ci unsigned int offset) 2513d0407baSopenharmony_ci{ 2523d0407baSopenharmony_ci return rockchip_gpio_set_direction(gc, offset, true); 2533d0407baSopenharmony_ci} 2543d0407baSopenharmony_ci 2553d0407baSopenharmony_cistatic int rockchip_gpio_direction_output(struct gpio_chip *gc, 2563d0407baSopenharmony_ci unsigned int offset, int value) 2573d0407baSopenharmony_ci{ 2583d0407baSopenharmony_ci rockchip_gpio_set(gc, offset, value); 2593d0407baSopenharmony_ci 2603d0407baSopenharmony_ci return rockchip_gpio_set_direction(gc, offset, false); 2613d0407baSopenharmony_ci} 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_ci/* 2643d0407baSopenharmony_ci * gpiolib set_config callback function. The setting of the pin 2653d0407baSopenharmony_ci * mux function as 'gpio output' will be handled by the pinctrl subsystem 2663d0407baSopenharmony_ci * interface. 2673d0407baSopenharmony_ci */ 2683d0407baSopenharmony_cistatic int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset, 2693d0407baSopenharmony_ci unsigned long config) 2703d0407baSopenharmony_ci{ 2713d0407baSopenharmony_ci enum pin_config_param param = pinconf_to_config_param(config); 2723d0407baSopenharmony_ci unsigned int debounce = pinconf_to_config_argument(config); 2733d0407baSopenharmony_ci int ret = 0; 2743d0407baSopenharmony_ci 2753d0407baSopenharmony_ci switch (param) { 2763d0407baSopenharmony_ci case PIN_CONFIG_INPUT_DEBOUNCE: 2773d0407baSopenharmony_ci /* 2783d0407baSopenharmony_ci * Rockchip's gpio could only support up to one period 2793d0407baSopenharmony_ci * of the debounce clock(pclk), which is far away from 2803d0407baSopenharmony_ci * satisftying the requirement, as pclk is usually near 2813d0407baSopenharmony_ci * 100MHz shared by all peripherals. So the fact is it 2823d0407baSopenharmony_ci * has crippled debounce capability could only be useful 2833d0407baSopenharmony_ci * to prevent any spurious glitches from waking up the system 2843d0407baSopenharmony_ci * if the gpio is conguired as wakeup interrupt source. Let's 2853d0407baSopenharmony_ci * still return -ENOTSUPP as before, to make sure the caller 2863d0407baSopenharmony_ci * of gpiod_set_debounce won't change its behaviour. 2873d0407baSopenharmony_ci */ 2883d0407baSopenharmony_ci rockchip_gpio_set_debounce(gc, offset, debounce); 2893d0407baSopenharmony_ci ret = -ENOTSUPP; 2903d0407baSopenharmony_ci break; 2913d0407baSopenharmony_ci default: 2923d0407baSopenharmony_ci ret = -ENOTSUPP; 2933d0407baSopenharmony_ci break; 2943d0407baSopenharmony_ci } 2953d0407baSopenharmony_ci 2963d0407baSopenharmony_ci return ret; 2973d0407baSopenharmony_ci} 2983d0407baSopenharmony_ci 2993d0407baSopenharmony_ci/* 3003d0407baSopenharmony_ci * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin 3013d0407baSopenharmony_ci * and a virtual IRQ, if not already present. 3023d0407baSopenharmony_ci */ 3033d0407baSopenharmony_cistatic int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) 3043d0407baSopenharmony_ci{ 3053d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 3063d0407baSopenharmony_ci unsigned int virq; 3073d0407baSopenharmony_ci 3083d0407baSopenharmony_ci if (!bank->domain) 3093d0407baSopenharmony_ci return -ENXIO; 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_ci virq = irq_create_mapping(bank->domain, offset); 3123d0407baSopenharmony_ci 3133d0407baSopenharmony_ci return (virq) ? : -ENXIO; 3143d0407baSopenharmony_ci} 3153d0407baSopenharmony_ci 3163d0407baSopenharmony_cistatic const struct gpio_chip rockchip_gpiolib_chip = { 3173d0407baSopenharmony_ci .request = gpiochip_generic_request, 3183d0407baSopenharmony_ci .free = gpiochip_generic_free, 3193d0407baSopenharmony_ci .set = rockchip_gpio_set, 3203d0407baSopenharmony_ci .get = rockchip_gpio_get, 3213d0407baSopenharmony_ci .get_direction = rockchip_gpio_get_direction, 3223d0407baSopenharmony_ci .direction_input = rockchip_gpio_direction_input, 3233d0407baSopenharmony_ci .direction_output = rockchip_gpio_direction_output, 3243d0407baSopenharmony_ci .set_config = rockchip_gpio_set_config, 3253d0407baSopenharmony_ci .to_irq = rockchip_gpio_to_irq, 3263d0407baSopenharmony_ci .owner = THIS_MODULE, 3273d0407baSopenharmony_ci}; 3283d0407baSopenharmony_ci 3293d0407baSopenharmony_cistatic void rockchip_irq_demux(struct irq_desc *desc) 3303d0407baSopenharmony_ci{ 3313d0407baSopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 3323d0407baSopenharmony_ci struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc); 3333d0407baSopenharmony_ci u32 pend; 3343d0407baSopenharmony_ci 3353d0407baSopenharmony_ci dev_dbg(bank->dev, "got irq for bank %s\n", bank->name); 3363d0407baSopenharmony_ci 3373d0407baSopenharmony_ci chained_irq_enter(chip, desc); 3383d0407baSopenharmony_ci 3393d0407baSopenharmony_ci pend = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status); 3403d0407baSopenharmony_ci 3413d0407baSopenharmony_ci while (pend) { 3423d0407baSopenharmony_ci unsigned int irq, virq; 3433d0407baSopenharmony_ci 3443d0407baSopenharmony_ci irq = __ffs(pend); 3453d0407baSopenharmony_ci pend &= ~BIT(irq); 3463d0407baSopenharmony_ci virq = irq_find_mapping(bank->domain, irq); 3473d0407baSopenharmony_ci 3483d0407baSopenharmony_ci if (!virq) { 3493d0407baSopenharmony_ci dev_err(bank->dev, "unmapped irq %d\n", irq); 3503d0407baSopenharmony_ci continue; 3513d0407baSopenharmony_ci } 3523d0407baSopenharmony_ci 3533d0407baSopenharmony_ci dev_dbg(bank->dev, "handling irq %d\n", irq); 3543d0407baSopenharmony_ci 3553d0407baSopenharmony_ci /* 3563d0407baSopenharmony_ci * Triggering IRQ on both rising and falling edge 3573d0407baSopenharmony_ci * needs manual intervention. 3583d0407baSopenharmony_ci */ 3593d0407baSopenharmony_ci if (bank->toggle_edge_mode & BIT(irq)) { 3603d0407baSopenharmony_ci u32 data, data_old, polarity; 3613d0407baSopenharmony_ci unsigned long flags; 3623d0407baSopenharmony_ci 3633d0407baSopenharmony_ci data = readl_relaxed(bank->reg_base + 3643d0407baSopenharmony_ci bank->gpio_regs->ext_port); 3653d0407baSopenharmony_ci do { 3663d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 3673d0407baSopenharmony_ci 3683d0407baSopenharmony_ci polarity = readl_relaxed(bank->reg_base + 3693d0407baSopenharmony_ci bank->gpio_regs->int_polarity); 3703d0407baSopenharmony_ci if (data & BIT(irq)) 3713d0407baSopenharmony_ci polarity &= ~BIT(irq); 3723d0407baSopenharmony_ci else 3733d0407baSopenharmony_ci polarity |= BIT(irq); 3743d0407baSopenharmony_ci writel(polarity, 3753d0407baSopenharmony_ci bank->reg_base + 3763d0407baSopenharmony_ci bank->gpio_regs->int_polarity); 3773d0407baSopenharmony_ci 3783d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 3793d0407baSopenharmony_ci 3803d0407baSopenharmony_ci data_old = data; 3813d0407baSopenharmony_ci data = readl_relaxed(bank->reg_base + 3823d0407baSopenharmony_ci bank->gpio_regs->ext_port); 3833d0407baSopenharmony_ci } while ((data & BIT(irq)) != (data_old & BIT(irq))); 3843d0407baSopenharmony_ci } 3853d0407baSopenharmony_ci 3863d0407baSopenharmony_ci generic_handle_irq(virq); 3873d0407baSopenharmony_ci } 3883d0407baSopenharmony_ci 3893d0407baSopenharmony_ci chained_irq_exit(chip, desc); 3903d0407baSopenharmony_ci} 3913d0407baSopenharmony_ci 3923d0407baSopenharmony_cistatic int rockchip_irq_set_type(struct irq_data *d, unsigned int type) 3933d0407baSopenharmony_ci{ 3943d0407baSopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3953d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 3963d0407baSopenharmony_ci u32 mask = BIT(d->hwirq); 3973d0407baSopenharmony_ci u32 polarity; 3983d0407baSopenharmony_ci u32 level; 3993d0407baSopenharmony_ci u32 data; 4003d0407baSopenharmony_ci unsigned long flags; 4013d0407baSopenharmony_ci int ret = 0; 4023d0407baSopenharmony_ci 4033d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 4043d0407baSopenharmony_ci 4053d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, d->hwirq, 0, 4063d0407baSopenharmony_ci bank->gpio_regs->port_ddr); 4073d0407baSopenharmony_ci 4083d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 4093d0407baSopenharmony_ci 4103d0407baSopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 4113d0407baSopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 4123d0407baSopenharmony_ci else 4133d0407baSopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 4143d0407baSopenharmony_ci 4153d0407baSopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 4163d0407baSopenharmony_ci 4173d0407baSopenharmony_ci level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type); 4183d0407baSopenharmony_ci polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity); 4193d0407baSopenharmony_ci 4203d0407baSopenharmony_ci switch (type) { 4213d0407baSopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 4223d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 4233d0407baSopenharmony_ci bank->toggle_edge_mode &= ~mask; 4243d0407baSopenharmony_ci rockchip_gpio_writel_bit(bank, d->hwirq, 1, 4253d0407baSopenharmony_ci bank->gpio_regs->int_bothedge); 4263d0407baSopenharmony_ci goto out; 4273d0407baSopenharmony_ci } else { 4283d0407baSopenharmony_ci bank->toggle_edge_mode |= mask; 4293d0407baSopenharmony_ci level |= mask; 4303d0407baSopenharmony_ci 4313d0407baSopenharmony_ci /* 4323d0407baSopenharmony_ci * Determine gpio state. If 1 next interrupt should be 4333d0407baSopenharmony_ci * falling otherwise rising. 4343d0407baSopenharmony_ci */ 4353d0407baSopenharmony_ci data = readl(bank->reg_base + bank->gpio_regs->ext_port); 4363d0407baSopenharmony_ci if (data & mask) 4373d0407baSopenharmony_ci polarity &= ~mask; 4383d0407baSopenharmony_ci else 4393d0407baSopenharmony_ci polarity |= mask; 4403d0407baSopenharmony_ci } 4413d0407baSopenharmony_ci break; 4423d0407baSopenharmony_ci case IRQ_TYPE_EDGE_RISING: 4433d0407baSopenharmony_ci bank->toggle_edge_mode &= ~mask; 4443d0407baSopenharmony_ci level |= mask; 4453d0407baSopenharmony_ci polarity |= mask; 4463d0407baSopenharmony_ci break; 4473d0407baSopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 4483d0407baSopenharmony_ci bank->toggle_edge_mode &= ~mask; 4493d0407baSopenharmony_ci level |= mask; 4503d0407baSopenharmony_ci polarity &= ~mask; 4513d0407baSopenharmony_ci break; 4523d0407baSopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 4533d0407baSopenharmony_ci bank->toggle_edge_mode &= ~mask; 4543d0407baSopenharmony_ci level &= ~mask; 4553d0407baSopenharmony_ci polarity |= mask; 4563d0407baSopenharmony_ci break; 4573d0407baSopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 4583d0407baSopenharmony_ci bank->toggle_edge_mode &= ~mask; 4593d0407baSopenharmony_ci level &= ~mask; 4603d0407baSopenharmony_ci polarity &= ~mask; 4613d0407baSopenharmony_ci break; 4623d0407baSopenharmony_ci default: 4633d0407baSopenharmony_ci ret = -EINVAL; 4643d0407baSopenharmony_ci goto out; 4653d0407baSopenharmony_ci } 4663d0407baSopenharmony_ci 4673d0407baSopenharmony_ci rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type); 4683d0407baSopenharmony_ci rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity); 4693d0407baSopenharmony_ciout: 4703d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 4713d0407baSopenharmony_ci 4723d0407baSopenharmony_ci return ret; 4733d0407baSopenharmony_ci} 4743d0407baSopenharmony_ci 4753d0407baSopenharmony_cistatic void rockchip_irq_suspend(struct irq_data *d) 4763d0407baSopenharmony_ci{ 4773d0407baSopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 4783d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 4793d0407baSopenharmony_ci 4803d0407baSopenharmony_ci bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask); 4813d0407baSopenharmony_ci irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask); 4823d0407baSopenharmony_ci} 4833d0407baSopenharmony_ci 4843d0407baSopenharmony_cistatic void rockchip_irq_resume(struct irq_data *d) 4853d0407baSopenharmony_ci{ 4863d0407baSopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 4873d0407baSopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 4883d0407baSopenharmony_ci 4893d0407baSopenharmony_ci irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask); 4903d0407baSopenharmony_ci} 4913d0407baSopenharmony_ci 4923d0407baSopenharmony_cistatic void rockchip_irq_enable(struct irq_data *d) 4933d0407baSopenharmony_ci{ 4943d0407baSopenharmony_ci irq_gc_mask_clr_bit(d); 4953d0407baSopenharmony_ci} 4963d0407baSopenharmony_ci 4973d0407baSopenharmony_cistatic void rockchip_irq_disable(struct irq_data *d) 4983d0407baSopenharmony_ci{ 4993d0407baSopenharmony_ci irq_gc_mask_set_bit(d); 5003d0407baSopenharmony_ci} 5013d0407baSopenharmony_ci 5023d0407baSopenharmony_cistatic int rockchip_interrupts_register(struct rockchip_pin_bank *bank) 5033d0407baSopenharmony_ci{ 5043d0407baSopenharmony_ci unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 5053d0407baSopenharmony_ci struct irq_chip_generic *gc; 5063d0407baSopenharmony_ci int ret; 5073d0407baSopenharmony_ci 5083d0407baSopenharmony_ci bank->domain = irq_domain_add_linear(bank->of_node, 32, 5093d0407baSopenharmony_ci &irq_generic_chip_ops, NULL); 5103d0407baSopenharmony_ci if (!bank->domain) { 5113d0407baSopenharmony_ci dev_warn(bank->dev, "could not init irq domain for bank %s\n", 5123d0407baSopenharmony_ci bank->name); 5133d0407baSopenharmony_ci return -EINVAL; 5143d0407baSopenharmony_ci } 5153d0407baSopenharmony_ci 5163d0407baSopenharmony_ci ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1, 5173d0407baSopenharmony_ci "rockchip_gpio_irq", 5183d0407baSopenharmony_ci handle_level_irq, 5193d0407baSopenharmony_ci clr, 0, 0); 5203d0407baSopenharmony_ci if (ret) { 5213d0407baSopenharmony_ci dev_err(bank->dev, "could not alloc generic chips for bank %s\n", 5223d0407baSopenharmony_ci bank->name); 5233d0407baSopenharmony_ci irq_domain_remove(bank->domain); 5243d0407baSopenharmony_ci return -EINVAL; 5253d0407baSopenharmony_ci } 5263d0407baSopenharmony_ci 5273d0407baSopenharmony_ci gc = irq_get_domain_generic_chip(bank->domain, 0); 5283d0407baSopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 5293d0407baSopenharmony_ci gc->reg_writel = gpio_writel_v2; 5303d0407baSopenharmony_ci gc->reg_readl = gpio_readl_v2; 5313d0407baSopenharmony_ci } 5323d0407baSopenharmony_ci 5333d0407baSopenharmony_ci gc->reg_base = bank->reg_base; 5343d0407baSopenharmony_ci gc->private = bank; 5353d0407baSopenharmony_ci gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask; 5363d0407baSopenharmony_ci gc->chip_types[0].regs.ack = bank->gpio_regs->port_eoi; 5373d0407baSopenharmony_ci gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 5383d0407baSopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; 5393d0407baSopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; 5403d0407baSopenharmony_ci gc->chip_types[0].chip.irq_enable = rockchip_irq_enable; 5413d0407baSopenharmony_ci gc->chip_types[0].chip.irq_disable = rockchip_irq_disable; 5423d0407baSopenharmony_ci gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; 5433d0407baSopenharmony_ci gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; 5443d0407baSopenharmony_ci gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; 5453d0407baSopenharmony_ci gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; 5463d0407baSopenharmony_ci gc->wake_enabled = IRQ_MSK(bank->nr_pins); 5473d0407baSopenharmony_ci 5483d0407baSopenharmony_ci /* 5493d0407baSopenharmony_ci * Linux assumes that all interrupts start out disabled/masked. 5503d0407baSopenharmony_ci * Our driver only uses the concept of masked and always keeps 5513d0407baSopenharmony_ci * things enabled, so for us that's all masked and all enabled. 5523d0407baSopenharmony_ci */ 5533d0407baSopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_mask); 5543d0407baSopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->port_eoi); 5553d0407baSopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_en); 5563d0407baSopenharmony_ci gc->mask_cache = 0xffffffff; 5573d0407baSopenharmony_ci 5583d0407baSopenharmony_ci irq_set_chained_handler_and_data(bank->irq, 5593d0407baSopenharmony_ci rockchip_irq_demux, bank); 5603d0407baSopenharmony_ci 5613d0407baSopenharmony_ci return 0; 5623d0407baSopenharmony_ci} 5633d0407baSopenharmony_ci 5643d0407baSopenharmony_cistatic int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) 5653d0407baSopenharmony_ci{ 5663d0407baSopenharmony_ci struct gpio_chip *gc; 5673d0407baSopenharmony_ci int ret; 5683d0407baSopenharmony_ci 5693d0407baSopenharmony_ci bank->gpio_chip = rockchip_gpiolib_chip; 5703d0407baSopenharmony_ci 5713d0407baSopenharmony_ci gc = &bank->gpio_chip; 5723d0407baSopenharmony_ci gc->base = bank->pin_base; 5733d0407baSopenharmony_ci gc->ngpio = bank->nr_pins; 5743d0407baSopenharmony_ci gc->label = bank->name; 5753d0407baSopenharmony_ci gc->parent = bank->dev; 5763d0407baSopenharmony_ci#ifdef CONFIG_OF_GPIO 5773d0407baSopenharmony_ci gc->of_node = of_node_get(bank->of_node); 5783d0407baSopenharmony_ci#endif 5793d0407baSopenharmony_ci 5803d0407baSopenharmony_ci ret = gpiochip_add_data(gc, bank); 5813d0407baSopenharmony_ci if (ret) { 5823d0407baSopenharmony_ci dev_err(bank->dev, "failed to add gpiochip %s, %d\n", 5833d0407baSopenharmony_ci gc->label, ret); 5843d0407baSopenharmony_ci return ret; 5853d0407baSopenharmony_ci } 5863d0407baSopenharmony_ci 5873d0407baSopenharmony_ci /* 5883d0407baSopenharmony_ci * For DeviceTree-supported systems, the gpio core checks the 5893d0407baSopenharmony_ci * pinctrl's device node for the "gpio-ranges" property. 5903d0407baSopenharmony_ci * If it is present, it takes care of adding the pin ranges 5913d0407baSopenharmony_ci * for the driver. In this case the driver can skip ahead. 5923d0407baSopenharmony_ci * 5933d0407baSopenharmony_ci * In order to remain compatible with older, existing DeviceTree 5943d0407baSopenharmony_ci * files which don't set the "gpio-ranges" property or systems that 5953d0407baSopenharmony_ci * utilize ACPI the driver has to call gpiochip_add_pin_range(). 5963d0407baSopenharmony_ci */ 5973d0407baSopenharmony_ci if (!of_property_read_bool(bank->of_node, "gpio-ranges")) { 5983d0407baSopenharmony_ci struct device_node *pctlnp = of_get_parent(bank->of_node); 5993d0407baSopenharmony_ci struct pinctrl_dev *pctldev = NULL; 6003d0407baSopenharmony_ci 6013d0407baSopenharmony_ci if (!pctlnp) 6023d0407baSopenharmony_ci return -ENODATA; 6033d0407baSopenharmony_ci 6043d0407baSopenharmony_ci pctldev = of_pinctrl_get(pctlnp); 6053d0407baSopenharmony_ci if (!pctldev) 6063d0407baSopenharmony_ci return -ENODEV; 6073d0407baSopenharmony_ci 6083d0407baSopenharmony_ci ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, 6093d0407baSopenharmony_ci gc->base, gc->ngpio); 6103d0407baSopenharmony_ci if (ret) { 6113d0407baSopenharmony_ci dev_err(bank->dev, "Failed to add pin range\n"); 6123d0407baSopenharmony_ci goto fail; 6133d0407baSopenharmony_ci } 6143d0407baSopenharmony_ci } 6153d0407baSopenharmony_ci 6163d0407baSopenharmony_ci ret = rockchip_interrupts_register(bank); 6173d0407baSopenharmony_ci if (ret) { 6183d0407baSopenharmony_ci dev_err(bank->dev, "failed to register interrupt, %d\n", ret); 6193d0407baSopenharmony_ci goto fail; 6203d0407baSopenharmony_ci } 6213d0407baSopenharmony_ci 6223d0407baSopenharmony_ci return 0; 6233d0407baSopenharmony_ci 6243d0407baSopenharmony_cifail: 6253d0407baSopenharmony_ci gpiochip_remove(&bank->gpio_chip); 6263d0407baSopenharmony_ci 6273d0407baSopenharmony_ci return ret; 6283d0407baSopenharmony_ci} 6293d0407baSopenharmony_ci 6303d0407baSopenharmony_cistatic int rockchip_get_bank_data(struct rockchip_pin_bank *bank) 6313d0407baSopenharmony_ci{ 6323d0407baSopenharmony_ci struct resource res; 6333d0407baSopenharmony_ci int id = 0; 6343d0407baSopenharmony_ci 6353d0407baSopenharmony_ci if (of_address_to_resource(bank->of_node, 0, &res)) { 6363d0407baSopenharmony_ci dev_err(bank->dev, "cannot find IO resource for bank\n"); 6373d0407baSopenharmony_ci return -ENOENT; 6383d0407baSopenharmony_ci } 6393d0407baSopenharmony_ci 6403d0407baSopenharmony_ci bank->reg_base = devm_ioremap_resource(bank->dev, &res); 6413d0407baSopenharmony_ci if (IS_ERR(bank->reg_base)) 6423d0407baSopenharmony_ci return PTR_ERR(bank->reg_base); 6433d0407baSopenharmony_ci 6443d0407baSopenharmony_ci bank->irq = irq_of_parse_and_map(bank->of_node, 0); 6453d0407baSopenharmony_ci if (!bank->irq) 6463d0407baSopenharmony_ci return -EINVAL; 6473d0407baSopenharmony_ci 6483d0407baSopenharmony_ci bank->clk = of_clk_get(bank->of_node, 0); 6493d0407baSopenharmony_ci if (IS_ERR(bank->clk)) 6503d0407baSopenharmony_ci return PTR_ERR(bank->clk); 6513d0407baSopenharmony_ci 6523d0407baSopenharmony_ci clk_prepare_enable(bank->clk); 6533d0407baSopenharmony_ci id = readl(bank->reg_base + gpio_regs_v2.version_id); 6543d0407baSopenharmony_ci 6553d0407baSopenharmony_ci /* If not gpio v2, that is default to v1. */ 6563d0407baSopenharmony_ci if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { 6573d0407baSopenharmony_ci bank->gpio_regs = &gpio_regs_v2; 6583d0407baSopenharmony_ci bank->gpio_type = GPIO_TYPE_V2; 6593d0407baSopenharmony_ci bank->db_clk = of_clk_get(bank->of_node, 1); 6603d0407baSopenharmony_ci if (IS_ERR(bank->db_clk)) { 6613d0407baSopenharmony_ci dev_err(bank->dev, "cannot find debounce clk\n"); 6623d0407baSopenharmony_ci clk_disable_unprepare(bank->clk); 6633d0407baSopenharmony_ci return -EINVAL; 6643d0407baSopenharmony_ci } 6653d0407baSopenharmony_ci } else { 6663d0407baSopenharmony_ci bank->gpio_regs = &gpio_regs_v1; 6673d0407baSopenharmony_ci bank->gpio_type = GPIO_TYPE_V1; 6683d0407baSopenharmony_ci } 6693d0407baSopenharmony_ci 6703d0407baSopenharmony_ci return 0; 6713d0407baSopenharmony_ci} 6723d0407baSopenharmony_ci 6733d0407baSopenharmony_cistatic struct rockchip_pin_bank * 6743d0407baSopenharmony_cirockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id) 6753d0407baSopenharmony_ci{ 6763d0407baSopenharmony_ci struct rockchip_pinctrl *info; 6773d0407baSopenharmony_ci struct rockchip_pin_bank *bank; 6783d0407baSopenharmony_ci int i, found = 0; 6793d0407baSopenharmony_ci 6803d0407baSopenharmony_ci info = pinctrl_dev_get_drvdata(pctldev); 6813d0407baSopenharmony_ci bank = info->ctrl->pin_banks; 6823d0407baSopenharmony_ci for (i = 0; i < info->ctrl->nr_banks; i++, bank++) { 6833d0407baSopenharmony_ci if (bank->bank_num == id) { 6843d0407baSopenharmony_ci found = 1; 6853d0407baSopenharmony_ci break; 6863d0407baSopenharmony_ci } 6873d0407baSopenharmony_ci } 6883d0407baSopenharmony_ci 6893d0407baSopenharmony_ci return found ? bank : NULL; 6903d0407baSopenharmony_ci} 6913d0407baSopenharmony_ci 6923d0407baSopenharmony_cistatic int rockchip_gpio_probe(struct platform_device *pdev) 6933d0407baSopenharmony_ci{ 6943d0407baSopenharmony_ci struct device *dev = &pdev->dev; 6953d0407baSopenharmony_ci struct device_node *np = dev->of_node; 6963d0407baSopenharmony_ci struct device_node *pctlnp = of_get_parent(np); 6973d0407baSopenharmony_ci struct pinctrl_dev *pctldev = NULL; 6983d0407baSopenharmony_ci struct rockchip_pin_bank *bank = NULL; 6993d0407baSopenharmony_ci struct rockchip_pin_output_deferred *cfg; 7003d0407baSopenharmony_ci static int gpio; 7013d0407baSopenharmony_ci int id, ret; 7023d0407baSopenharmony_ci 7033d0407baSopenharmony_ci if (!np || !pctlnp) 7043d0407baSopenharmony_ci return -ENODEV; 7053d0407baSopenharmony_ci 7063d0407baSopenharmony_ci pctldev = of_pinctrl_get(pctlnp); 7073d0407baSopenharmony_ci if (!pctldev) 7083d0407baSopenharmony_ci return -EPROBE_DEFER; 7093d0407baSopenharmony_ci 7103d0407baSopenharmony_ci id = of_alias_get_id(np, "gpio"); 7113d0407baSopenharmony_ci if (id < 0) 7123d0407baSopenharmony_ci id = gpio++; 7133d0407baSopenharmony_ci 7143d0407baSopenharmony_ci bank = rockchip_gpio_find_bank(pctldev, id); 7153d0407baSopenharmony_ci if (!bank) 7163d0407baSopenharmony_ci return -EINVAL; 7173d0407baSopenharmony_ci 7183d0407baSopenharmony_ci bank->dev = dev; 7193d0407baSopenharmony_ci bank->of_node = np; 7203d0407baSopenharmony_ci 7213d0407baSopenharmony_ci raw_spin_lock_init(&bank->slock); 7223d0407baSopenharmony_ci 7233d0407baSopenharmony_ci ret = rockchip_get_bank_data(bank); 7243d0407baSopenharmony_ci if (ret) 7253d0407baSopenharmony_ci return ret; 7263d0407baSopenharmony_ci 7273d0407baSopenharmony_ci /* 7283d0407baSopenharmony_ci * Prevent clashes with a deferred output setting 7293d0407baSopenharmony_ci * being added right at this moment. 7303d0407baSopenharmony_ci */ 7313d0407baSopenharmony_ci mutex_lock(&bank->deferred_lock); 7323d0407baSopenharmony_ci 7333d0407baSopenharmony_ci ret = rockchip_gpiolib_register(bank); 7343d0407baSopenharmony_ci if (ret) { 7353d0407baSopenharmony_ci clk_disable_unprepare(bank->clk); 7363d0407baSopenharmony_ci mutex_unlock(&bank->deferred_lock); 7373d0407baSopenharmony_ci return ret; 7383d0407baSopenharmony_ci } 7393d0407baSopenharmony_ci 7403d0407baSopenharmony_ci while (!list_empty(&bank->deferred_output)) { 7413d0407baSopenharmony_ci cfg = list_first_entry(&bank->deferred_output, 7423d0407baSopenharmony_ci struct rockchip_pin_output_deferred, head); 7433d0407baSopenharmony_ci list_del(&cfg->head); 7443d0407baSopenharmony_ci 7453d0407baSopenharmony_ci ret = rockchip_gpio_direction_output(&bank->gpio_chip, cfg->pin, cfg->arg); 7463d0407baSopenharmony_ci if (ret) 7473d0407baSopenharmony_ci dev_warn(dev, "setting output pin %u to %u failed\n", cfg->pin, cfg->arg); 7483d0407baSopenharmony_ci 7493d0407baSopenharmony_ci kfree(cfg); 7503d0407baSopenharmony_ci } 7513d0407baSopenharmony_ci 7523d0407baSopenharmony_ci mutex_unlock(&bank->deferred_lock); 7533d0407baSopenharmony_ci 7543d0407baSopenharmony_ci platform_set_drvdata(pdev, bank); 7553d0407baSopenharmony_ci dev_info(dev, "probed %pOF\n", np); 7563d0407baSopenharmony_ci 7573d0407baSopenharmony_ci return 0; 7583d0407baSopenharmony_ci} 7593d0407baSopenharmony_ci 7603d0407baSopenharmony_cistatic int rockchip_gpio_remove(struct platform_device *pdev) 7613d0407baSopenharmony_ci{ 7623d0407baSopenharmony_ci struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); 7633d0407baSopenharmony_ci 7643d0407baSopenharmony_ci clk_disable_unprepare(bank->clk); 7653d0407baSopenharmony_ci gpiochip_remove(&bank->gpio_chip); 7663d0407baSopenharmony_ci 7673d0407baSopenharmony_ci return 0; 7683d0407baSopenharmony_ci} 7693d0407baSopenharmony_ci 7703d0407baSopenharmony_cistatic const struct of_device_id rockchip_gpio_match[] = { 7713d0407baSopenharmony_ci { .compatible = "rockchip,gpio-bank", }, 7723d0407baSopenharmony_ci { .compatible = "rockchip,rk3188-gpio-bank0" }, 7733d0407baSopenharmony_ci { }, 7743d0407baSopenharmony_ci}; 7753d0407baSopenharmony_ci 7763d0407baSopenharmony_cistatic struct platform_driver rockchip_gpio_driver = { 7773d0407baSopenharmony_ci .probe = rockchip_gpio_probe, 7783d0407baSopenharmony_ci .remove = rockchip_gpio_remove, 7793d0407baSopenharmony_ci .driver = { 7803d0407baSopenharmony_ci .name = "rockchip-gpio", 7813d0407baSopenharmony_ci .of_match_table = rockchip_gpio_match, 7823d0407baSopenharmony_ci }, 7833d0407baSopenharmony_ci}; 7843d0407baSopenharmony_ci 7853d0407baSopenharmony_cistatic int __init rockchip_gpio_init(void) 7863d0407baSopenharmony_ci{ 7873d0407baSopenharmony_ci return platform_driver_register(&rockchip_gpio_driver); 7883d0407baSopenharmony_ci} 7893d0407baSopenharmony_cipostcore_initcall(rockchip_gpio_init); 7903d0407baSopenharmony_ci 7913d0407baSopenharmony_cistatic void __exit rockchip_gpio_exit(void) 7923d0407baSopenharmony_ci{ 7933d0407baSopenharmony_ci platform_driver_unregister(&rockchip_gpio_driver); 7943d0407baSopenharmony_ci} 7953d0407baSopenharmony_cimodule_exit(rockchip_gpio_exit); 7963d0407baSopenharmony_ci 7973d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip gpio driver"); 7983d0407baSopenharmony_ciMODULE_ALIAS("platform:rockchip-gpio"); 7993d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 8003d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_gpio_match); 801