162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013 MundoReader S.L. 462306a36Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co. Ltd. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "../pinctrl/core.h" 2762306a36Sopenharmony_ci#include "../pinctrl/pinctrl-rockchip.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ 3062306a36Sopenharmony_ci#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ 3162306a36Sopenharmony_ci#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v1 = { 3462306a36Sopenharmony_ci .port_dr = 0x00, 3562306a36Sopenharmony_ci .port_ddr = 0x04, 3662306a36Sopenharmony_ci .int_en = 0x30, 3762306a36Sopenharmony_ci .int_mask = 0x34, 3862306a36Sopenharmony_ci .int_type = 0x38, 3962306a36Sopenharmony_ci .int_polarity = 0x3c, 4062306a36Sopenharmony_ci .int_status = 0x40, 4162306a36Sopenharmony_ci .int_rawstatus = 0x44, 4262306a36Sopenharmony_ci .debounce = 0x48, 4362306a36Sopenharmony_ci .port_eoi = 0x4c, 4462306a36Sopenharmony_ci .ext_port = 0x50, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v2 = { 4862306a36Sopenharmony_ci .port_dr = 0x00, 4962306a36Sopenharmony_ci .port_ddr = 0x08, 5062306a36Sopenharmony_ci .int_en = 0x10, 5162306a36Sopenharmony_ci .int_mask = 0x18, 5262306a36Sopenharmony_ci .int_type = 0x20, 5362306a36Sopenharmony_ci .int_polarity = 0x28, 5462306a36Sopenharmony_ci .int_bothedge = 0x30, 5562306a36Sopenharmony_ci .int_status = 0x50, 5662306a36Sopenharmony_ci .int_rawstatus = 0x58, 5762306a36Sopenharmony_ci .debounce = 0x38, 5862306a36Sopenharmony_ci .dbclk_div_en = 0x40, 5962306a36Sopenharmony_ci .dbclk_div_con = 0x48, 6062306a36Sopenharmony_ci .port_eoi = 0x60, 6162306a36Sopenharmony_ci .ext_port = 0x70, 6262306a36Sopenharmony_ci .version_id = 0x78, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline void gpio_writel_v2(u32 val, void __iomem *reg) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci writel((val & 0xffff) | 0xffff0000, reg); 6862306a36Sopenharmony_ci writel((val >> 16) | 0xffff0000, reg + 0x4); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline u32 gpio_readl_v2(void __iomem *reg) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return readl(reg + 0x4) << 16 | readl(reg); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank, 7762306a36Sopenharmony_ci u32 value, unsigned int offset) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci void __iomem *reg = bank->reg_base + offset; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) 8262306a36Sopenharmony_ci gpio_writel_v2(value, reg); 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci writel(value, reg); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank, 8862306a36Sopenharmony_ci unsigned int offset) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci void __iomem *reg = bank->reg_base + offset; 9162306a36Sopenharmony_ci u32 value; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) 9462306a36Sopenharmony_ci value = gpio_readl_v2(reg); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci value = readl(reg); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return value; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank, 10262306a36Sopenharmony_ci u32 bit, u32 value, 10362306a36Sopenharmony_ci unsigned int offset) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci void __iomem *reg = bank->reg_base + offset; 10662306a36Sopenharmony_ci u32 data; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 10962306a36Sopenharmony_ci if (value) 11062306a36Sopenharmony_ci data = BIT(bit % 16) | BIT(bit % 16 + 16); 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci data = BIT(bit % 16 + 16); 11362306a36Sopenharmony_ci writel(data, bit >= 16 ? reg + 0x4 : reg); 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci data = readl(reg); 11662306a36Sopenharmony_ci data &= ~BIT(bit); 11762306a36Sopenharmony_ci if (value) 11862306a36Sopenharmony_ci data |= BIT(bit); 11962306a36Sopenharmony_ci writel(data, reg); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank, 12462306a36Sopenharmony_ci u32 bit, unsigned int offset) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci void __iomem *reg = bank->reg_base + offset; 12762306a36Sopenharmony_ci u32 data; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 13062306a36Sopenharmony_ci data = readl(bit >= 16 ? reg + 0x4 : reg); 13162306a36Sopenharmony_ci data >>= bit % 16; 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci data = readl(reg); 13462306a36Sopenharmony_ci data >>= bit; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return data & (0x1); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int rockchip_gpio_get_direction(struct gpio_chip *chip, 14162306a36Sopenharmony_ci unsigned int offset) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(chip); 14462306a36Sopenharmony_ci u32 data; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr); 14762306a36Sopenharmony_ci if (data) 14862306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int rockchip_gpio_set_direction(struct gpio_chip *chip, 15462306a36Sopenharmony_ci unsigned int offset, bool input) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(chip); 15762306a36Sopenharmony_ci unsigned long flags; 15862306a36Sopenharmony_ci u32 data = input ? 0 : 1; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (input) 16262306a36Sopenharmony_ci pinctrl_gpio_direction_input(bank->pin_base + offset); 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci pinctrl_gpio_direction_output(bank->pin_base + offset); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 16762306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr); 16862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, 17462306a36Sopenharmony_ci int value) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 18062306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr); 18162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 18762306a36Sopenharmony_ci u32 data; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci data = readl(bank->reg_base + bank->gpio_regs->ext_port); 19062306a36Sopenharmony_ci data >>= offset; 19162306a36Sopenharmony_ci data &= 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return data; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int rockchip_gpio_set_debounce(struct gpio_chip *gc, 19762306a36Sopenharmony_ci unsigned int offset, 19862306a36Sopenharmony_ci unsigned int debounce) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 20162306a36Sopenharmony_ci const struct rockchip_gpio_regs *reg = bank->gpio_regs; 20262306a36Sopenharmony_ci unsigned long flags, div_reg, freq, max_debounce; 20362306a36Sopenharmony_ci bool div_debounce_support; 20462306a36Sopenharmony_ci unsigned int cur_div_reg; 20562306a36Sopenharmony_ci u64 div; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2 && !IS_ERR(bank->db_clk)) { 20862306a36Sopenharmony_ci div_debounce_support = true; 20962306a36Sopenharmony_ci freq = clk_get_rate(bank->db_clk); 21062306a36Sopenharmony_ci max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq; 21162306a36Sopenharmony_ci if (debounce > max_debounce) 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci div = debounce * freq; 21562306a36Sopenharmony_ci div_reg = DIV_ROUND_CLOSEST_ULL(div, 2 * USEC_PER_SEC) - 1; 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci div_debounce_support = false; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Only the v1 needs to configure div_en and div_con for dbclk */ 22362306a36Sopenharmony_ci if (debounce) { 22462306a36Sopenharmony_ci if (div_debounce_support) { 22562306a36Sopenharmony_ci /* Configure the max debounce from consumers */ 22662306a36Sopenharmony_ci cur_div_reg = readl(bank->reg_base + 22762306a36Sopenharmony_ci reg->dbclk_div_con); 22862306a36Sopenharmony_ci if (cur_div_reg < div_reg) 22962306a36Sopenharmony_ci writel(div_reg, bank->reg_base + 23062306a36Sopenharmony_ci reg->dbclk_div_con); 23162306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 1, 23262306a36Sopenharmony_ci reg->dbclk_div_en); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 1, reg->debounce); 23662306a36Sopenharmony_ci } else { 23762306a36Sopenharmony_ci if (div_debounce_support) 23862306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 0, 23962306a36Sopenharmony_ci reg->dbclk_div_en); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, offset, 0, reg->debounce); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Enable or disable dbclk at last */ 24762306a36Sopenharmony_ci if (div_debounce_support) { 24862306a36Sopenharmony_ci if (debounce) 24962306a36Sopenharmony_ci clk_prepare_enable(bank->db_clk); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci clk_disable_unprepare(bank->db_clk); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int rockchip_gpio_direction_input(struct gpio_chip *gc, 25862306a36Sopenharmony_ci unsigned int offset) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return rockchip_gpio_set_direction(gc, offset, true); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int rockchip_gpio_direction_output(struct gpio_chip *gc, 26462306a36Sopenharmony_ci unsigned int offset, int value) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci rockchip_gpio_set(gc, offset, value); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return rockchip_gpio_set_direction(gc, offset, false); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * gpiolib set_config callback function. The setting of the pin 27362306a36Sopenharmony_ci * mux function as 'gpio output' will be handled by the pinctrl subsystem 27462306a36Sopenharmony_ci * interface. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset, 27762306a36Sopenharmony_ci unsigned long config) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(config); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (param) { 28262306a36Sopenharmony_ci case PIN_CONFIG_INPUT_DEBOUNCE: 28362306a36Sopenharmony_ci rockchip_gpio_set_debounce(gc, offset, true); 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Rockchip's gpio could only support up to one period 28662306a36Sopenharmony_ci * of the debounce clock(pclk), which is far away from 28762306a36Sopenharmony_ci * satisftying the requirement, as pclk is usually near 28862306a36Sopenharmony_ci * 100MHz shared by all peripherals. So the fact is it 28962306a36Sopenharmony_ci * has crippled debounce capability could only be useful 29062306a36Sopenharmony_ci * to prevent any spurious glitches from waking up the system 29162306a36Sopenharmony_ci * if the gpio is conguired as wakeup interrupt source. Let's 29262306a36Sopenharmony_ci * still return -ENOTSUPP as before, to make sure the caller 29362306a36Sopenharmony_ci * of gpiod_set_debounce won't change its behaviour. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci return -ENOTSUPP; 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci return -ENOTSUPP; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * gpiod_to_irq() callback function. Creates a mapping between a GPIO pin 30362306a36Sopenharmony_ci * and a virtual IRQ, if not already present. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gpiochip_get_data(gc); 30862306a36Sopenharmony_ci unsigned int virq; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!bank->domain) 31162306a36Sopenharmony_ci return -ENXIO; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci virq = irq_create_mapping(bank->domain, offset); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return (virq) ? : -ENXIO; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct gpio_chip rockchip_gpiolib_chip = { 31962306a36Sopenharmony_ci .request = gpiochip_generic_request, 32062306a36Sopenharmony_ci .free = gpiochip_generic_free, 32162306a36Sopenharmony_ci .set = rockchip_gpio_set, 32262306a36Sopenharmony_ci .get = rockchip_gpio_get, 32362306a36Sopenharmony_ci .get_direction = rockchip_gpio_get_direction, 32462306a36Sopenharmony_ci .direction_input = rockchip_gpio_direction_input, 32562306a36Sopenharmony_ci .direction_output = rockchip_gpio_direction_output, 32662306a36Sopenharmony_ci .set_config = rockchip_gpio_set_config, 32762306a36Sopenharmony_ci .to_irq = rockchip_gpio_to_irq, 32862306a36Sopenharmony_ci .owner = THIS_MODULE, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void rockchip_irq_demux(struct irq_desc *desc) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 33462306a36Sopenharmony_ci struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc); 33562306a36Sopenharmony_ci unsigned long pending; 33662306a36Sopenharmony_ci unsigned int irq; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci dev_dbg(bank->dev, "got irq for bank %s\n", bank->name); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci chained_irq_enter(chip, desc); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci pending = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status); 34362306a36Sopenharmony_ci for_each_set_bit(irq, &pending, 32) { 34462306a36Sopenharmony_ci dev_dbg(bank->dev, "handling irq %d\n", irq); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * Triggering IRQ on both rising and falling edge 34862306a36Sopenharmony_ci * needs manual intervention. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (bank->toggle_edge_mode & BIT(irq)) { 35162306a36Sopenharmony_ci u32 data, data_old, polarity; 35262306a36Sopenharmony_ci unsigned long flags; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci data = readl_relaxed(bank->reg_base + 35562306a36Sopenharmony_ci bank->gpio_regs->ext_port); 35662306a36Sopenharmony_ci do { 35762306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci polarity = readl_relaxed(bank->reg_base + 36062306a36Sopenharmony_ci bank->gpio_regs->int_polarity); 36162306a36Sopenharmony_ci if (data & BIT(irq)) 36262306a36Sopenharmony_ci polarity &= ~BIT(irq); 36362306a36Sopenharmony_ci else 36462306a36Sopenharmony_ci polarity |= BIT(irq); 36562306a36Sopenharmony_ci writel(polarity, 36662306a36Sopenharmony_ci bank->reg_base + 36762306a36Sopenharmony_ci bank->gpio_regs->int_polarity); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci data_old = data; 37262306a36Sopenharmony_ci data = readl_relaxed(bank->reg_base + 37362306a36Sopenharmony_ci bank->gpio_regs->ext_port); 37462306a36Sopenharmony_ci } while ((data & BIT(irq)) != (data_old & BIT(irq))); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci generic_handle_domain_irq(bank->domain, irq); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci chained_irq_exit(chip, desc); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int rockchip_irq_set_type(struct irq_data *d, unsigned int type) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 38662306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 38762306a36Sopenharmony_ci u32 mask = BIT(d->hwirq); 38862306a36Sopenharmony_ci u32 polarity; 38962306a36Sopenharmony_ci u32 level; 39062306a36Sopenharmony_ci u32 data; 39162306a36Sopenharmony_ci unsigned long flags; 39262306a36Sopenharmony_ci int ret = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, d->hwirq, 0, 39762306a36Sopenharmony_ci bank->gpio_regs->port_ddr); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 40262306a36Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 40362306a36Sopenharmony_ci else 40462306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type); 40962306a36Sopenharmony_ci polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (type == IRQ_TYPE_EDGE_BOTH) { 41262306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 41362306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, d->hwirq, 1, 41462306a36Sopenharmony_ci bank->gpio_regs->int_bothedge); 41562306a36Sopenharmony_ci goto out; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci bank->toggle_edge_mode |= mask; 41862306a36Sopenharmony_ci level &= ~mask; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * Determine gpio state. If 1 next interrupt should be 42262306a36Sopenharmony_ci * low otherwise high. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci data = readl(bank->reg_base + bank->gpio_regs->ext_port); 42562306a36Sopenharmony_ci if (data & mask) 42662306a36Sopenharmony_ci polarity &= ~mask; 42762306a36Sopenharmony_ci else 42862306a36Sopenharmony_ci polarity |= mask; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } else { 43162306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 43262306a36Sopenharmony_ci rockchip_gpio_writel_bit(bank, d->hwirq, 0, 43362306a36Sopenharmony_ci bank->gpio_regs->int_bothedge); 43462306a36Sopenharmony_ci } else { 43562306a36Sopenharmony_ci bank->toggle_edge_mode &= ~mask; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci switch (type) { 43862306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 43962306a36Sopenharmony_ci level |= mask; 44062306a36Sopenharmony_ci polarity |= mask; 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 44362306a36Sopenharmony_ci level |= mask; 44462306a36Sopenharmony_ci polarity &= ~mask; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 44762306a36Sopenharmony_ci level &= ~mask; 44862306a36Sopenharmony_ci polarity |= mask; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 45162306a36Sopenharmony_ci level &= ~mask; 45262306a36Sopenharmony_ci polarity &= ~mask; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci default: 45562306a36Sopenharmony_ci ret = -EINVAL; 45662306a36Sopenharmony_ci goto out; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type); 46162306a36Sopenharmony_ci rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity); 46262306a36Sopenharmony_ciout: 46362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic int rockchip_irq_reqres(struct irq_data *d) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 47162306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return gpiochip_reqres_irq(&bank->gpio_chip, d->hwirq); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void rockchip_irq_relres(struct irq_data *d) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 47962306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci gpiochip_relres_irq(&bank->gpio_chip, d->hwirq); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic void rockchip_irq_suspend(struct irq_data *d) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 48762306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask); 49062306a36Sopenharmony_ci irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void rockchip_irq_resume(struct irq_data *d) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 49662306a36Sopenharmony_ci struct rockchip_pin_bank *bank = gc->private; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void rockchip_irq_enable(struct irq_data *d) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci irq_gc_mask_clr_bit(d); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void rockchip_irq_disable(struct irq_data *d) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci irq_gc_mask_set_bit(d); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int rockchip_interrupts_register(struct rockchip_pin_bank *bank) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 51462306a36Sopenharmony_ci struct irq_chip_generic *gc; 51562306a36Sopenharmony_ci int ret; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci bank->domain = irq_domain_add_linear(bank->of_node, 32, 51862306a36Sopenharmony_ci &irq_generic_chip_ops, NULL); 51962306a36Sopenharmony_ci if (!bank->domain) { 52062306a36Sopenharmony_ci dev_warn(bank->dev, "could not init irq domain for bank %s\n", 52162306a36Sopenharmony_ci bank->name); 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1, 52662306a36Sopenharmony_ci "rockchip_gpio_irq", 52762306a36Sopenharmony_ci handle_level_irq, 52862306a36Sopenharmony_ci clr, 0, 0); 52962306a36Sopenharmony_ci if (ret) { 53062306a36Sopenharmony_ci dev_err(bank->dev, "could not alloc generic chips for bank %s\n", 53162306a36Sopenharmony_ci bank->name); 53262306a36Sopenharmony_ci irq_domain_remove(bank->domain); 53362306a36Sopenharmony_ci return -EINVAL; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci gc = irq_get_domain_generic_chip(bank->domain, 0); 53762306a36Sopenharmony_ci if (bank->gpio_type == GPIO_TYPE_V2) { 53862306a36Sopenharmony_ci gc->reg_writel = gpio_writel_v2; 53962306a36Sopenharmony_ci gc->reg_readl = gpio_readl_v2; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci gc->reg_base = bank->reg_base; 54362306a36Sopenharmony_ci gc->private = bank; 54462306a36Sopenharmony_ci gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask; 54562306a36Sopenharmony_ci gc->chip_types[0].regs.ack = bank->gpio_regs->port_eoi; 54662306a36Sopenharmony_ci gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 54762306a36Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; 54862306a36Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; 54962306a36Sopenharmony_ci gc->chip_types[0].chip.irq_enable = rockchip_irq_enable; 55062306a36Sopenharmony_ci gc->chip_types[0].chip.irq_disable = rockchip_irq_disable; 55162306a36Sopenharmony_ci gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; 55262306a36Sopenharmony_ci gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; 55362306a36Sopenharmony_ci gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; 55462306a36Sopenharmony_ci gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; 55562306a36Sopenharmony_ci gc->chip_types[0].chip.irq_request_resources = rockchip_irq_reqres; 55662306a36Sopenharmony_ci gc->chip_types[0].chip.irq_release_resources = rockchip_irq_relres; 55762306a36Sopenharmony_ci gc->wake_enabled = IRQ_MSK(bank->nr_pins); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * Linux assumes that all interrupts start out disabled/masked. 56162306a36Sopenharmony_ci * Our driver only uses the concept of masked and always keeps 56262306a36Sopenharmony_ci * things enabled, so for us that's all masked and all enabled. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_mask); 56562306a36Sopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->port_eoi); 56662306a36Sopenharmony_ci rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_en); 56762306a36Sopenharmony_ci gc->mask_cache = 0xffffffff; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci irq_set_chained_handler_and_data(bank->irq, 57062306a36Sopenharmony_ci rockchip_irq_demux, bank); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct gpio_chip *gc; 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci bank->gpio_chip = rockchip_gpiolib_chip; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci gc = &bank->gpio_chip; 58362306a36Sopenharmony_ci gc->base = bank->pin_base; 58462306a36Sopenharmony_ci gc->ngpio = bank->nr_pins; 58562306a36Sopenharmony_ci gc->label = bank->name; 58662306a36Sopenharmony_ci gc->parent = bank->dev; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = gpiochip_add_data(gc, bank); 58962306a36Sopenharmony_ci if (ret) { 59062306a36Sopenharmony_ci dev_err(bank->dev, "failed to add gpiochip %s, %d\n", 59162306a36Sopenharmony_ci gc->label, ret); 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * For DeviceTree-supported systems, the gpio core checks the 59762306a36Sopenharmony_ci * pinctrl's device node for the "gpio-ranges" property. 59862306a36Sopenharmony_ci * If it is present, it takes care of adding the pin ranges 59962306a36Sopenharmony_ci * for the driver. In this case the driver can skip ahead. 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * In order to remain compatible with older, existing DeviceTree 60262306a36Sopenharmony_ci * files which don't set the "gpio-ranges" property or systems that 60362306a36Sopenharmony_ci * utilize ACPI the driver has to call gpiochip_add_pin_range(). 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci if (!of_property_read_bool(bank->of_node, "gpio-ranges")) { 60662306a36Sopenharmony_ci struct device_node *pctlnp = of_get_parent(bank->of_node); 60762306a36Sopenharmony_ci struct pinctrl_dev *pctldev = NULL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!pctlnp) 61062306a36Sopenharmony_ci return -ENODATA; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci pctldev = of_pinctrl_get(pctlnp); 61362306a36Sopenharmony_ci of_node_put(pctlnp); 61462306a36Sopenharmony_ci if (!pctldev) 61562306a36Sopenharmony_ci return -ENODEV; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, 61862306a36Sopenharmony_ci gc->base, gc->ngpio); 61962306a36Sopenharmony_ci if (ret) { 62062306a36Sopenharmony_ci dev_err(bank->dev, "Failed to add pin range\n"); 62162306a36Sopenharmony_ci goto fail; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = rockchip_interrupts_register(bank); 62662306a36Sopenharmony_ci if (ret) { 62762306a36Sopenharmony_ci dev_err(bank->dev, "failed to register interrupt, %d\n", ret); 62862306a36Sopenharmony_ci goto fail; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cifail: 63462306a36Sopenharmony_ci gpiochip_remove(&bank->gpio_chip); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int rockchip_get_bank_data(struct rockchip_pin_bank *bank) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct resource res; 64262306a36Sopenharmony_ci int id = 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (of_address_to_resource(bank->of_node, 0, &res)) { 64562306a36Sopenharmony_ci dev_err(bank->dev, "cannot find IO resource for bank\n"); 64662306a36Sopenharmony_ci return -ENOENT; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci bank->reg_base = devm_ioremap_resource(bank->dev, &res); 65062306a36Sopenharmony_ci if (IS_ERR(bank->reg_base)) 65162306a36Sopenharmony_ci return PTR_ERR(bank->reg_base); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci bank->irq = irq_of_parse_and_map(bank->of_node, 0); 65462306a36Sopenharmony_ci if (!bank->irq) 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci bank->clk = of_clk_get(bank->of_node, 0); 65862306a36Sopenharmony_ci if (IS_ERR(bank->clk)) 65962306a36Sopenharmony_ci return PTR_ERR(bank->clk); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci clk_prepare_enable(bank->clk); 66262306a36Sopenharmony_ci id = readl(bank->reg_base + gpio_regs_v2.version_id); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* If not gpio v2, that is default to v1. */ 66562306a36Sopenharmony_ci if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { 66662306a36Sopenharmony_ci bank->gpio_regs = &gpio_regs_v2; 66762306a36Sopenharmony_ci bank->gpio_type = GPIO_TYPE_V2; 66862306a36Sopenharmony_ci bank->db_clk = of_clk_get(bank->of_node, 1); 66962306a36Sopenharmony_ci if (IS_ERR(bank->db_clk)) { 67062306a36Sopenharmony_ci dev_err(bank->dev, "cannot find debounce clk\n"); 67162306a36Sopenharmony_ci clk_disable_unprepare(bank->clk); 67262306a36Sopenharmony_ci return -EINVAL; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci } else { 67562306a36Sopenharmony_ci bank->gpio_regs = &gpio_regs_v1; 67662306a36Sopenharmony_ci bank->gpio_type = GPIO_TYPE_V1; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic struct rockchip_pin_bank * 68362306a36Sopenharmony_cirockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct rockchip_pinctrl *info; 68662306a36Sopenharmony_ci struct rockchip_pin_bank *bank; 68762306a36Sopenharmony_ci int i, found = 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci info = pinctrl_dev_get_drvdata(pctldev); 69062306a36Sopenharmony_ci bank = info->ctrl->pin_banks; 69162306a36Sopenharmony_ci for (i = 0; i < info->ctrl->nr_banks; i++, bank++) { 69262306a36Sopenharmony_ci if (bank->bank_num == id) { 69362306a36Sopenharmony_ci found = 1; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return found ? bank : NULL; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int rockchip_gpio_probe(struct platform_device *pdev) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 70562306a36Sopenharmony_ci struct device_node *pctlnp = of_get_parent(np); 70662306a36Sopenharmony_ci struct pinctrl_dev *pctldev = NULL; 70762306a36Sopenharmony_ci struct rockchip_pin_bank *bank = NULL; 70862306a36Sopenharmony_ci struct rockchip_pin_deferred *cfg; 70962306a36Sopenharmony_ci static int gpio; 71062306a36Sopenharmony_ci int id, ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (!np || !pctlnp) 71362306a36Sopenharmony_ci return -ENODEV; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci pctldev = of_pinctrl_get(pctlnp); 71662306a36Sopenharmony_ci if (!pctldev) 71762306a36Sopenharmony_ci return -EPROBE_DEFER; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci id = of_alias_get_id(np, "gpio"); 72062306a36Sopenharmony_ci if (id < 0) 72162306a36Sopenharmony_ci id = gpio++; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci bank = rockchip_gpio_find_bank(pctldev, id); 72462306a36Sopenharmony_ci if (!bank) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci bank->dev = dev; 72862306a36Sopenharmony_ci bank->of_node = np; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci raw_spin_lock_init(&bank->slock); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = rockchip_get_bank_data(bank); 73362306a36Sopenharmony_ci if (ret) 73462306a36Sopenharmony_ci return ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* 73762306a36Sopenharmony_ci * Prevent clashes with a deferred output setting 73862306a36Sopenharmony_ci * being added right at this moment. 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci mutex_lock(&bank->deferred_lock); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci ret = rockchip_gpiolib_register(bank); 74362306a36Sopenharmony_ci if (ret) { 74462306a36Sopenharmony_ci clk_disable_unprepare(bank->clk); 74562306a36Sopenharmony_ci mutex_unlock(&bank->deferred_lock); 74662306a36Sopenharmony_ci return ret; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci while (!list_empty(&bank->deferred_pins)) { 75062306a36Sopenharmony_ci cfg = list_first_entry(&bank->deferred_pins, 75162306a36Sopenharmony_ci struct rockchip_pin_deferred, head); 75262306a36Sopenharmony_ci list_del(&cfg->head); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci switch (cfg->param) { 75562306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 75662306a36Sopenharmony_ci ret = rockchip_gpio_direction_output(&bank->gpio_chip, cfg->pin, cfg->arg); 75762306a36Sopenharmony_ci if (ret) 75862306a36Sopenharmony_ci dev_warn(dev, "setting output pin %u to %u failed\n", cfg->pin, 75962306a36Sopenharmony_ci cfg->arg); 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 76262306a36Sopenharmony_ci ret = rockchip_gpio_direction_input(&bank->gpio_chip, cfg->pin); 76362306a36Sopenharmony_ci if (ret) 76462306a36Sopenharmony_ci dev_warn(dev, "setting input pin %u failed\n", cfg->pin); 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci default: 76762306a36Sopenharmony_ci dev_warn(dev, "unknown deferred config param %d\n", cfg->param); 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci kfree(cfg); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci mutex_unlock(&bank->deferred_lock); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci platform_set_drvdata(pdev, bank); 77662306a36Sopenharmony_ci dev_info(dev, "probed %pOF\n", np); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int rockchip_gpio_remove(struct platform_device *pdev) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci clk_disable_unprepare(bank->clk); 78662306a36Sopenharmony_ci gpiochip_remove(&bank->gpio_chip); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic const struct of_device_id rockchip_gpio_match[] = { 79262306a36Sopenharmony_ci { .compatible = "rockchip,gpio-bank", }, 79362306a36Sopenharmony_ci { .compatible = "rockchip,rk3188-gpio-bank0" }, 79462306a36Sopenharmony_ci { }, 79562306a36Sopenharmony_ci}; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic struct platform_driver rockchip_gpio_driver = { 79862306a36Sopenharmony_ci .probe = rockchip_gpio_probe, 79962306a36Sopenharmony_ci .remove = rockchip_gpio_remove, 80062306a36Sopenharmony_ci .driver = { 80162306a36Sopenharmony_ci .name = "rockchip-gpio", 80262306a36Sopenharmony_ci .of_match_table = rockchip_gpio_match, 80362306a36Sopenharmony_ci }, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int __init rockchip_gpio_init(void) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci return platform_driver_register(&rockchip_gpio_driver); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_cipostcore_initcall(rockchip_gpio_init); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void __exit rockchip_gpio_exit(void) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci platform_driver_unregister(&rockchip_gpio_driver); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_cimodule_exit(rockchip_gpio_exit); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip gpio driver"); 81962306a36Sopenharmony_ciMODULE_ALIAS("platform:rockchip-gpio"); 82062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 82162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_gpio_match); 822