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