162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2015 IBM Corp. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Joel Stanley <joel@jms.id.au> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/gpio/aspeed.h> 1062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1162306a36Sopenharmony_ci#include <linux/hashtable.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/seq_file.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/string.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/div64.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * These two headers aren't meant to be used by GPIO drivers. We need 2662306a36Sopenharmony_ci * them in order to access gpio_chip_hwgpio() which we need to implement 2762306a36Sopenharmony_ci * the aspeed specific API which allows the coprocessor to request 2862306a36Sopenharmony_ci * access to some GPIOs and to arbitrate between coprocessor and ARM. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 3162306a36Sopenharmony_ci#include "gpiolib.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct aspeed_bank_props { 3462306a36Sopenharmony_ci unsigned int bank; 3562306a36Sopenharmony_ci u32 input; 3662306a36Sopenharmony_ci u32 output; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct aspeed_gpio_config { 4062306a36Sopenharmony_ci unsigned int nr_gpios; 4162306a36Sopenharmony_ci const struct aspeed_bank_props *props; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * @offset_timer: Maps an offset to an @timer_users index, or zero if disabled 4662306a36Sopenharmony_ci * @timer_users: Tracks the number of users for each timer 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * The @timer_users has four elements but the first element is unused. This is 4962306a36Sopenharmony_ci * to simplify accounting and indexing, as a zero value in @offset_timer 5062306a36Sopenharmony_ci * represents disabled debouncing for the GPIO. Any other value for an element 5162306a36Sopenharmony_ci * of @offset_timer is used as an index into @timer_users. This behaviour of 5262306a36Sopenharmony_ci * the zero value aligns with the behaviour of zero built from the timer 5362306a36Sopenharmony_ci * configuration registers (i.e. debouncing is disabled). 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct aspeed_gpio { 5662306a36Sopenharmony_ci struct gpio_chip chip; 5762306a36Sopenharmony_ci struct device *dev; 5862306a36Sopenharmony_ci raw_spinlock_t lock; 5962306a36Sopenharmony_ci void __iomem *base; 6062306a36Sopenharmony_ci int irq; 6162306a36Sopenharmony_ci const struct aspeed_gpio_config *config; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci u8 *offset_timer; 6462306a36Sopenharmony_ci unsigned int timer_users[4]; 6562306a36Sopenharmony_ci struct clk *clk; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci u32 *dcache; 6862306a36Sopenharmony_ci u8 *cf_copro_bankmap; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct aspeed_gpio_bank { 7262306a36Sopenharmony_ci uint16_t val_regs; /* +0: Rd: read input value, Wr: set write latch 7362306a36Sopenharmony_ci * +4: Rd/Wr: Direction (0=in, 1=out) 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci uint16_t rdata_reg; /* Rd: read write latch, Wr: <none> */ 7662306a36Sopenharmony_ci uint16_t irq_regs; 7762306a36Sopenharmony_ci uint16_t debounce_regs; 7862306a36Sopenharmony_ci uint16_t tolerance_regs; 7962306a36Sopenharmony_ci uint16_t cmdsrc_regs; 8062306a36Sopenharmony_ci const char names[4][3]; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * Note: The "value" register returns the input value sampled on the 8562306a36Sopenharmony_ci * line even when the GPIO is configured as an output. Since 8662306a36Sopenharmony_ci * that input goes through synchronizers, writing, then reading 8762306a36Sopenharmony_ci * back may not return the written value right away. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * The "rdata" register returns the content of the write latch 9062306a36Sopenharmony_ci * and thus can be used to read back what was last written 9162306a36Sopenharmony_ci * reliably. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 }; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct aspeed_gpio_copro_ops *copro_ops; 9762306a36Sopenharmony_cistatic void *copro_data; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct aspeed_gpio_bank aspeed_gpio_banks[] = { 10062306a36Sopenharmony_ci { 10162306a36Sopenharmony_ci .val_regs = 0x0000, 10262306a36Sopenharmony_ci .rdata_reg = 0x00c0, 10362306a36Sopenharmony_ci .irq_regs = 0x0008, 10462306a36Sopenharmony_ci .debounce_regs = 0x0040, 10562306a36Sopenharmony_ci .tolerance_regs = 0x001c, 10662306a36Sopenharmony_ci .cmdsrc_regs = 0x0060, 10762306a36Sopenharmony_ci .names = { "A", "B", "C", "D" }, 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci { 11062306a36Sopenharmony_ci .val_regs = 0x0020, 11162306a36Sopenharmony_ci .rdata_reg = 0x00c4, 11262306a36Sopenharmony_ci .irq_regs = 0x0028, 11362306a36Sopenharmony_ci .debounce_regs = 0x0048, 11462306a36Sopenharmony_ci .tolerance_regs = 0x003c, 11562306a36Sopenharmony_ci .cmdsrc_regs = 0x0068, 11662306a36Sopenharmony_ci .names = { "E", "F", "G", "H" }, 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci { 11962306a36Sopenharmony_ci .val_regs = 0x0070, 12062306a36Sopenharmony_ci .rdata_reg = 0x00c8, 12162306a36Sopenharmony_ci .irq_regs = 0x0098, 12262306a36Sopenharmony_ci .debounce_regs = 0x00b0, 12362306a36Sopenharmony_ci .tolerance_regs = 0x00ac, 12462306a36Sopenharmony_ci .cmdsrc_regs = 0x0090, 12562306a36Sopenharmony_ci .names = { "I", "J", "K", "L" }, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci .val_regs = 0x0078, 12962306a36Sopenharmony_ci .rdata_reg = 0x00cc, 13062306a36Sopenharmony_ci .irq_regs = 0x00e8, 13162306a36Sopenharmony_ci .debounce_regs = 0x0100, 13262306a36Sopenharmony_ci .tolerance_regs = 0x00fc, 13362306a36Sopenharmony_ci .cmdsrc_regs = 0x00e0, 13462306a36Sopenharmony_ci .names = { "M", "N", "O", "P" }, 13562306a36Sopenharmony_ci }, 13662306a36Sopenharmony_ci { 13762306a36Sopenharmony_ci .val_regs = 0x0080, 13862306a36Sopenharmony_ci .rdata_reg = 0x00d0, 13962306a36Sopenharmony_ci .irq_regs = 0x0118, 14062306a36Sopenharmony_ci .debounce_regs = 0x0130, 14162306a36Sopenharmony_ci .tolerance_regs = 0x012c, 14262306a36Sopenharmony_ci .cmdsrc_regs = 0x0110, 14362306a36Sopenharmony_ci .names = { "Q", "R", "S", "T" }, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci .val_regs = 0x0088, 14762306a36Sopenharmony_ci .rdata_reg = 0x00d4, 14862306a36Sopenharmony_ci .irq_regs = 0x0148, 14962306a36Sopenharmony_ci .debounce_regs = 0x0160, 15062306a36Sopenharmony_ci .tolerance_regs = 0x015c, 15162306a36Sopenharmony_ci .cmdsrc_regs = 0x0140, 15262306a36Sopenharmony_ci .names = { "U", "V", "W", "X" }, 15362306a36Sopenharmony_ci }, 15462306a36Sopenharmony_ci { 15562306a36Sopenharmony_ci .val_regs = 0x01E0, 15662306a36Sopenharmony_ci .rdata_reg = 0x00d8, 15762306a36Sopenharmony_ci .irq_regs = 0x0178, 15862306a36Sopenharmony_ci .debounce_regs = 0x0190, 15962306a36Sopenharmony_ci .tolerance_regs = 0x018c, 16062306a36Sopenharmony_ci .cmdsrc_regs = 0x0170, 16162306a36Sopenharmony_ci .names = { "Y", "Z", "AA", "AB" }, 16262306a36Sopenharmony_ci }, 16362306a36Sopenharmony_ci { 16462306a36Sopenharmony_ci .val_regs = 0x01e8, 16562306a36Sopenharmony_ci .rdata_reg = 0x00dc, 16662306a36Sopenharmony_ci .irq_regs = 0x01a8, 16762306a36Sopenharmony_ci .debounce_regs = 0x01c0, 16862306a36Sopenharmony_ci .tolerance_regs = 0x01bc, 16962306a36Sopenharmony_ci .cmdsrc_regs = 0x01a0, 17062306a36Sopenharmony_ci .names = { "AC", "", "", "" }, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cienum aspeed_gpio_reg { 17562306a36Sopenharmony_ci reg_val, 17662306a36Sopenharmony_ci reg_rdata, 17762306a36Sopenharmony_ci reg_dir, 17862306a36Sopenharmony_ci reg_irq_enable, 17962306a36Sopenharmony_ci reg_irq_type0, 18062306a36Sopenharmony_ci reg_irq_type1, 18162306a36Sopenharmony_ci reg_irq_type2, 18262306a36Sopenharmony_ci reg_irq_status, 18362306a36Sopenharmony_ci reg_debounce_sel1, 18462306a36Sopenharmony_ci reg_debounce_sel2, 18562306a36Sopenharmony_ci reg_tolerance, 18662306a36Sopenharmony_ci reg_cmdsrc0, 18762306a36Sopenharmony_ci reg_cmdsrc1, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#define GPIO_VAL_VALUE 0x00 19162306a36Sopenharmony_ci#define GPIO_VAL_DIR 0x04 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#define GPIO_IRQ_ENABLE 0x00 19462306a36Sopenharmony_ci#define GPIO_IRQ_TYPE0 0x04 19562306a36Sopenharmony_ci#define GPIO_IRQ_TYPE1 0x08 19662306a36Sopenharmony_ci#define GPIO_IRQ_TYPE2 0x0c 19762306a36Sopenharmony_ci#define GPIO_IRQ_STATUS 0x10 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#define GPIO_DEBOUNCE_SEL1 0x00 20062306a36Sopenharmony_ci#define GPIO_DEBOUNCE_SEL2 0x04 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci#define GPIO_CMDSRC_0 0x00 20362306a36Sopenharmony_ci#define GPIO_CMDSRC_1 0x04 20462306a36Sopenharmony_ci#define GPIO_CMDSRC_ARM 0 20562306a36Sopenharmony_ci#define GPIO_CMDSRC_LPC 1 20662306a36Sopenharmony_ci#define GPIO_CMDSRC_COLDFIRE 2 20762306a36Sopenharmony_ci#define GPIO_CMDSRC_RESERVED 3 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* This will be resolved at compile time */ 21062306a36Sopenharmony_cistatic inline void __iomem *bank_reg(struct aspeed_gpio *gpio, 21162306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank, 21262306a36Sopenharmony_ci const enum aspeed_gpio_reg reg) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci switch (reg) { 21562306a36Sopenharmony_ci case reg_val: 21662306a36Sopenharmony_ci return gpio->base + bank->val_regs + GPIO_VAL_VALUE; 21762306a36Sopenharmony_ci case reg_rdata: 21862306a36Sopenharmony_ci return gpio->base + bank->rdata_reg; 21962306a36Sopenharmony_ci case reg_dir: 22062306a36Sopenharmony_ci return gpio->base + bank->val_regs + GPIO_VAL_DIR; 22162306a36Sopenharmony_ci case reg_irq_enable: 22262306a36Sopenharmony_ci return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE; 22362306a36Sopenharmony_ci case reg_irq_type0: 22462306a36Sopenharmony_ci return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0; 22562306a36Sopenharmony_ci case reg_irq_type1: 22662306a36Sopenharmony_ci return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1; 22762306a36Sopenharmony_ci case reg_irq_type2: 22862306a36Sopenharmony_ci return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2; 22962306a36Sopenharmony_ci case reg_irq_status: 23062306a36Sopenharmony_ci return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS; 23162306a36Sopenharmony_ci case reg_debounce_sel1: 23262306a36Sopenharmony_ci return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL1; 23362306a36Sopenharmony_ci case reg_debounce_sel2: 23462306a36Sopenharmony_ci return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2; 23562306a36Sopenharmony_ci case reg_tolerance: 23662306a36Sopenharmony_ci return gpio->base + bank->tolerance_regs; 23762306a36Sopenharmony_ci case reg_cmdsrc0: 23862306a36Sopenharmony_ci return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_0; 23962306a36Sopenharmony_ci case reg_cmdsrc1: 24062306a36Sopenharmony_ci return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_1; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci BUG(); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci#define GPIO_BANK(x) ((x) >> 5) 24662306a36Sopenharmony_ci#define GPIO_OFFSET(x) ((x) & 0x1f) 24762306a36Sopenharmony_ci#define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci#define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o)) 25062306a36Sopenharmony_ci#define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1) 25162306a36Sopenharmony_ci#define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0) 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic const struct aspeed_gpio_bank *to_bank(unsigned int offset) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci unsigned int bank = GPIO_BANK(offset); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks)); 25862306a36Sopenharmony_ci return &aspeed_gpio_banks[bank]; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic inline bool is_bank_props_sentinel(const struct aspeed_bank_props *props) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci return !(props->input || props->output); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic inline const struct aspeed_bank_props *find_bank_props( 26762306a36Sopenharmony_ci struct aspeed_gpio *gpio, unsigned int offset) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci const struct aspeed_bank_props *props = gpio->config->props; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci while (!is_bank_props_sentinel(props)) { 27262306a36Sopenharmony_ci if (props->bank == GPIO_BANK(offset)) 27362306a36Sopenharmony_ci return props; 27462306a36Sopenharmony_ci props++; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return NULL; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic inline bool have_gpio(struct aspeed_gpio *gpio, unsigned int offset) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci const struct aspeed_bank_props *props = find_bank_props(gpio, offset); 28362306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 28462306a36Sopenharmony_ci unsigned int group = GPIO_OFFSET(offset) / 8; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return bank->names[group][0] != '\0' && 28762306a36Sopenharmony_ci (!props || ((props->input | props->output) & GPIO_BIT(offset))); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic inline bool have_input(struct aspeed_gpio *gpio, unsigned int offset) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci const struct aspeed_bank_props *props = find_bank_props(gpio, offset); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return !props || (props->input & GPIO_BIT(offset)); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci#define have_irq(g, o) have_input((g), (o)) 29862306a36Sopenharmony_ci#define have_debounce(g, o) have_input((g), (o)) 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci const struct aspeed_bank_props *props = find_bank_props(gpio, offset); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return !props || (props->output & GPIO_BIT(offset)); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio, 30862306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank, 30962306a36Sopenharmony_ci int bindex, int cmdsrc) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci void __iomem *c0 = bank_reg(gpio, bank, reg_cmdsrc0); 31262306a36Sopenharmony_ci void __iomem *c1 = bank_reg(gpio, bank, reg_cmdsrc1); 31362306a36Sopenharmony_ci u32 bit, reg; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * Each register controls 4 banks, so take the bottom 2 31762306a36Sopenharmony_ci * bits of the bank index, and use them to select the 31862306a36Sopenharmony_ci * right control bit (0, 8, 16 or 24). 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci bit = BIT((bindex & 3) << 3); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Source 1 first to avoid illegal 11 combination */ 32362306a36Sopenharmony_ci reg = ioread32(c1); 32462306a36Sopenharmony_ci if (cmdsrc & 2) 32562306a36Sopenharmony_ci reg |= bit; 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci reg &= ~bit; 32862306a36Sopenharmony_ci iowrite32(reg, c1); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Then Source 0 */ 33162306a36Sopenharmony_ci reg = ioread32(c0); 33262306a36Sopenharmony_ci if (cmdsrc & 1) 33362306a36Sopenharmony_ci reg |= bit; 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci reg &= ~bit; 33662306a36Sopenharmony_ci iowrite32(reg, c0); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic bool aspeed_gpio_copro_request(struct aspeed_gpio *gpio, 34062306a36Sopenharmony_ci unsigned int offset) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!copro_ops || !gpio->cf_copro_bankmap) 34562306a36Sopenharmony_ci return false; 34662306a36Sopenharmony_ci if (!gpio->cf_copro_bankmap[offset >> 3]) 34762306a36Sopenharmony_ci return false; 34862306a36Sopenharmony_ci if (!copro_ops->request_access) 34962306a36Sopenharmony_ci return false; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Pause the coprocessor */ 35262306a36Sopenharmony_ci copro_ops->request_access(copro_data); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Change command source back to ARM */ 35562306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3, GPIO_CMDSRC_ARM); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Update cache */ 35862306a36Sopenharmony_ci gpio->dcache[GPIO_BANK(offset)] = ioread32(bank_reg(gpio, bank, reg_rdata)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return true; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void aspeed_gpio_copro_release(struct aspeed_gpio *gpio, 36462306a36Sopenharmony_ci unsigned int offset) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!copro_ops || !gpio->cf_copro_bankmap) 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci if (!gpio->cf_copro_bankmap[offset >> 3]) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci if (!copro_ops->release_access) 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Change command source back to ColdFire */ 37662306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3, 37762306a36Sopenharmony_ci GPIO_CMDSRC_COLDFIRE); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Restart the coprocessor */ 38062306a36Sopenharmony_ci copro_ops->release_access(copro_data); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 38662306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset)); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, 39262306a36Sopenharmony_ci int val) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 39562306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 39662306a36Sopenharmony_ci void __iomem *addr; 39762306a36Sopenharmony_ci u32 reg; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_val); 40062306a36Sopenharmony_ci reg = gpio->dcache[GPIO_BANK(offset)]; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (val) 40362306a36Sopenharmony_ci reg |= GPIO_BIT(offset); 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci reg &= ~GPIO_BIT(offset); 40662306a36Sopenharmony_ci gpio->dcache[GPIO_BANK(offset)] = reg; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci iowrite32(reg, addr); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, 41262306a36Sopenharmony_ci int val) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 41562306a36Sopenharmony_ci unsigned long flags; 41662306a36Sopenharmony_ci bool copro; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 41962306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci __aspeed_gpio_set(gc, offset, val); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (copro) 42462306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 42562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 43162306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 43262306a36Sopenharmony_ci void __iomem *addr = bank_reg(gpio, bank, reg_dir); 43362306a36Sopenharmony_ci unsigned long flags; 43462306a36Sopenharmony_ci bool copro; 43562306a36Sopenharmony_ci u32 reg; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!have_input(gpio, offset)) 43862306a36Sopenharmony_ci return -ENOTSUPP; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci reg = ioread32(addr); 44362306a36Sopenharmony_ci reg &= ~GPIO_BIT(offset); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 44662306a36Sopenharmony_ci iowrite32(reg, addr); 44762306a36Sopenharmony_ci if (copro) 44862306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int aspeed_gpio_dir_out(struct gpio_chip *gc, 45662306a36Sopenharmony_ci unsigned int offset, int val) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 45962306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 46062306a36Sopenharmony_ci void __iomem *addr = bank_reg(gpio, bank, reg_dir); 46162306a36Sopenharmony_ci unsigned long flags; 46262306a36Sopenharmony_ci bool copro; 46362306a36Sopenharmony_ci u32 reg; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!have_output(gpio, offset)) 46662306a36Sopenharmony_ci return -ENOTSUPP; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci reg = ioread32(addr); 47162306a36Sopenharmony_ci reg |= GPIO_BIT(offset); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 47462306a36Sopenharmony_ci __aspeed_gpio_set(gc, offset, val); 47562306a36Sopenharmony_ci iowrite32(reg, addr); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (copro) 47862306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 47962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 48762306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 48862306a36Sopenharmony_ci unsigned long flags; 48962306a36Sopenharmony_ci u32 val; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!have_input(gpio, offset)) 49262306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!have_output(gpio, offset)) 49562306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic inline int irqd_to_aspeed_gpio_data(struct irq_data *d, 50762306a36Sopenharmony_ci struct aspeed_gpio **gpio, 50862306a36Sopenharmony_ci const struct aspeed_gpio_bank **bank, 50962306a36Sopenharmony_ci u32 *bit, int *offset) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct aspeed_gpio *internal; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci *offset = irqd_to_hwirq(d); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci internal = irq_data_get_irq_chip_data(d); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* This might be a bit of a questionable place to check */ 51862306a36Sopenharmony_ci if (!have_irq(internal, *offset)) 51962306a36Sopenharmony_ci return -ENOTSUPP; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci *gpio = internal; 52262306a36Sopenharmony_ci *bank = to_bank(*offset); 52362306a36Sopenharmony_ci *bit = GPIO_BIT(*offset); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void aspeed_gpio_irq_ack(struct irq_data *d) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank; 53162306a36Sopenharmony_ci struct aspeed_gpio *gpio; 53262306a36Sopenharmony_ci unsigned long flags; 53362306a36Sopenharmony_ci void __iomem *status_addr; 53462306a36Sopenharmony_ci int rc, offset; 53562306a36Sopenharmony_ci bool copro; 53662306a36Sopenharmony_ci u32 bit; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); 53962306a36Sopenharmony_ci if (rc) 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci status_addr = bank_reg(gpio, bank, reg_irq_status); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 54562306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci iowrite32(bit, status_addr); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (copro) 55062306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 55162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank; 55762306a36Sopenharmony_ci struct aspeed_gpio *gpio; 55862306a36Sopenharmony_ci unsigned long flags; 55962306a36Sopenharmony_ci u32 reg, bit; 56062306a36Sopenharmony_ci void __iomem *addr; 56162306a36Sopenharmony_ci int rc, offset; 56262306a36Sopenharmony_ci bool copro; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); 56562306a36Sopenharmony_ci if (rc) 56662306a36Sopenharmony_ci return; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_irq_enable); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Unmasking the IRQ */ 57162306a36Sopenharmony_ci if (set) 57262306a36Sopenharmony_ci gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 57562306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci reg = ioread32(addr); 57862306a36Sopenharmony_ci if (set) 57962306a36Sopenharmony_ci reg |= bit; 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci reg &= ~bit; 58262306a36Sopenharmony_ci iowrite32(reg, addr); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (copro) 58562306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 58662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Masking the IRQ */ 58962306a36Sopenharmony_ci if (!set) 59062306a36Sopenharmony_ci gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(d)); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void aspeed_gpio_irq_mask(struct irq_data *d) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci aspeed_gpio_irq_set_mask(d, false); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void aspeed_gpio_irq_unmask(struct irq_data *d) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci aspeed_gpio_irq_set_mask(d, true); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci u32 type0 = 0; 60662306a36Sopenharmony_ci u32 type1 = 0; 60762306a36Sopenharmony_ci u32 type2 = 0; 60862306a36Sopenharmony_ci u32 bit, reg; 60962306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank; 61062306a36Sopenharmony_ci irq_flow_handler_t handler; 61162306a36Sopenharmony_ci struct aspeed_gpio *gpio; 61262306a36Sopenharmony_ci unsigned long flags; 61362306a36Sopenharmony_ci void __iomem *addr; 61462306a36Sopenharmony_ci int rc, offset; 61562306a36Sopenharmony_ci bool copro; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); 61862306a36Sopenharmony_ci if (rc) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 62262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 62362306a36Sopenharmony_ci type2 |= bit; 62462306a36Sopenharmony_ci fallthrough; 62562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 62662306a36Sopenharmony_ci type0 |= bit; 62762306a36Sopenharmony_ci fallthrough; 62862306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 62962306a36Sopenharmony_ci handler = handle_edge_irq; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 63262306a36Sopenharmony_ci type0 |= bit; 63362306a36Sopenharmony_ci fallthrough; 63462306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 63562306a36Sopenharmony_ci type1 |= bit; 63662306a36Sopenharmony_ci handler = handle_level_irq; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci default: 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 64362306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_irq_type0); 64662306a36Sopenharmony_ci reg = ioread32(addr); 64762306a36Sopenharmony_ci reg = (reg & ~bit) | type0; 64862306a36Sopenharmony_ci iowrite32(reg, addr); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_irq_type1); 65162306a36Sopenharmony_ci reg = ioread32(addr); 65262306a36Sopenharmony_ci reg = (reg & ~bit) | type1; 65362306a36Sopenharmony_ci iowrite32(reg, addr); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_irq_type2); 65662306a36Sopenharmony_ci reg = ioread32(addr); 65762306a36Sopenharmony_ci reg = (reg & ~bit) | type2; 65862306a36Sopenharmony_ci iowrite32(reg, addr); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (copro) 66162306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 66262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci irq_set_handler_locked(d, handler); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void aspeed_gpio_irq_handler(struct irq_desc *desc) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 67262306a36Sopenharmony_ci struct irq_chip *ic = irq_desc_get_chip(desc); 67362306a36Sopenharmony_ci struct aspeed_gpio *data = gpiochip_get_data(gc); 67462306a36Sopenharmony_ci unsigned int i, p, banks; 67562306a36Sopenharmony_ci unsigned long reg; 67662306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci chained_irq_enter(ic, desc); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); 68162306a36Sopenharmony_ci for (i = 0; i < banks; i++) { 68262306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci reg = ioread32(bank_reg(data, bank, reg_irq_status)); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci for_each_set_bit(p, ®, 32) 68762306a36Sopenharmony_ci generic_handle_domain_irq(gc->irq.domain, i * 32 + p); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci chained_irq_exit(ic, desc); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void aspeed_init_irq_valid_mask(struct gpio_chip *gc, 69462306a36Sopenharmony_ci unsigned long *valid_mask, 69562306a36Sopenharmony_ci unsigned int ngpios) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(gc); 69862306a36Sopenharmony_ci const struct aspeed_bank_props *props = gpio->config->props; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci while (!is_bank_props_sentinel(props)) { 70162306a36Sopenharmony_ci unsigned int offset; 70262306a36Sopenharmony_ci const unsigned long int input = props->input; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Pretty crummy approach, but similar to GPIO core */ 70562306a36Sopenharmony_ci for_each_clear_bit(offset, &input, 32) { 70662306a36Sopenharmony_ci unsigned int i = props->bank * 32 + offset; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (i >= gpio->chip.ngpio) 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci clear_bit(i, valid_mask); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci props++; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, 71962306a36Sopenharmony_ci unsigned int offset, bool enable) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 72262306a36Sopenharmony_ci unsigned long flags; 72362306a36Sopenharmony_ci void __iomem *treg; 72462306a36Sopenharmony_ci bool copro; 72562306a36Sopenharmony_ci u32 val; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci treg = bank_reg(gpio, to_bank(offset), reg_tolerance); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 73062306a36Sopenharmony_ci copro = aspeed_gpio_copro_request(gpio, offset); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci val = readl(treg); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (enable) 73562306a36Sopenharmony_ci val |= GPIO_BIT(offset); 73662306a36Sopenharmony_ci else 73762306a36Sopenharmony_ci val &= ~GPIO_BIT(offset); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci writel(val, treg); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (copro) 74262306a36Sopenharmony_ci aspeed_gpio_copro_release(gpio, offset); 74362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci if (!have_gpio(gpiochip_get_data(chip), offset)) 75162306a36Sopenharmony_ci return -ENODEV; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return pinctrl_gpio_request(chip->base + offset); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci pinctrl_gpio_free(chip->base + offset); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int usecs_to_cycles(struct aspeed_gpio *gpio, unsigned long usecs, 76262306a36Sopenharmony_ci u32 *cycles) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci u64 rate; 76562306a36Sopenharmony_ci u64 n; 76662306a36Sopenharmony_ci u32 r; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci rate = clk_get_rate(gpio->clk); 76962306a36Sopenharmony_ci if (!rate) 77062306a36Sopenharmony_ci return -ENOTSUPP; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci n = rate * usecs; 77362306a36Sopenharmony_ci r = do_div(n, 1000000); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (n >= U32_MAX) 77662306a36Sopenharmony_ci return -ERANGE; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* At least as long as the requested time */ 77962306a36Sopenharmony_ci *cycles = n + (!!r); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/* Call under gpio->lock */ 78562306a36Sopenharmony_cistatic int register_allocated_timer(struct aspeed_gpio *gpio, 78662306a36Sopenharmony_ci unsigned int offset, unsigned int timer) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci if (WARN(gpio->offset_timer[offset] != 0, 78962306a36Sopenharmony_ci "Offset %d already allocated timer %d\n", 79062306a36Sopenharmony_ci offset, gpio->offset_timer[offset])) 79162306a36Sopenharmony_ci return -EINVAL; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (WARN(gpio->timer_users[timer] == UINT_MAX, 79462306a36Sopenharmony_ci "Timer user count would overflow\n")) 79562306a36Sopenharmony_ci return -EPERM; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci gpio->offset_timer[offset] = timer; 79862306a36Sopenharmony_ci gpio->timer_users[timer]++; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci/* Call under gpio->lock */ 80462306a36Sopenharmony_cistatic int unregister_allocated_timer(struct aspeed_gpio *gpio, 80562306a36Sopenharmony_ci unsigned int offset) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci if (WARN(gpio->offset_timer[offset] == 0, 80862306a36Sopenharmony_ci "No timer allocated to offset %d\n", offset)) 80962306a36Sopenharmony_ci return -EINVAL; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (WARN(gpio->timer_users[gpio->offset_timer[offset]] == 0, 81262306a36Sopenharmony_ci "No users recorded for timer %d\n", 81362306a36Sopenharmony_ci gpio->offset_timer[offset])) 81462306a36Sopenharmony_ci return -EINVAL; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci gpio->timer_users[gpio->offset_timer[offset]]--; 81762306a36Sopenharmony_ci gpio->offset_timer[offset] = 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return 0; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* Call under gpio->lock */ 82362306a36Sopenharmony_cistatic inline bool timer_allocation_registered(struct aspeed_gpio *gpio, 82462306a36Sopenharmony_ci unsigned int offset) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci return gpio->offset_timer[offset] > 0; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/* Call under gpio->lock */ 83062306a36Sopenharmony_cistatic void configure_timer(struct aspeed_gpio *gpio, unsigned int offset, 83162306a36Sopenharmony_ci unsigned int timer) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 83462306a36Sopenharmony_ci const u32 mask = GPIO_BIT(offset); 83562306a36Sopenharmony_ci void __iomem *addr; 83662306a36Sopenharmony_ci u32 val; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Note: Debounce timer isn't under control of the command 83962306a36Sopenharmony_ci * source registers, so no need to sync with the coprocessor 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_debounce_sel1); 84262306a36Sopenharmony_ci val = ioread32(addr); 84362306a36Sopenharmony_ci iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci addr = bank_reg(gpio, bank, reg_debounce_sel2); 84662306a36Sopenharmony_ci val = ioread32(addr); 84762306a36Sopenharmony_ci iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int enable_debounce(struct gpio_chip *chip, unsigned int offset, 85162306a36Sopenharmony_ci unsigned long usecs) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 85462306a36Sopenharmony_ci u32 requested_cycles; 85562306a36Sopenharmony_ci unsigned long flags; 85662306a36Sopenharmony_ci int rc; 85762306a36Sopenharmony_ci int i; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (!gpio->clk) 86062306a36Sopenharmony_ci return -EINVAL; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci rc = usecs_to_cycles(gpio, usecs, &requested_cycles); 86362306a36Sopenharmony_ci if (rc < 0) { 86462306a36Sopenharmony_ci dev_warn(chip->parent, "Failed to convert %luus to cycles at %luHz: %d\n", 86562306a36Sopenharmony_ci usecs, clk_get_rate(gpio->clk), rc); 86662306a36Sopenharmony_ci return rc; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (timer_allocation_registered(gpio, offset)) { 87262306a36Sopenharmony_ci rc = unregister_allocated_timer(gpio, offset); 87362306a36Sopenharmony_ci if (rc < 0) 87462306a36Sopenharmony_ci goto out; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Try to find a timer already configured for the debounce period */ 87862306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(debounce_timers); i++) { 87962306a36Sopenharmony_ci u32 cycles; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci cycles = ioread32(gpio->base + debounce_timers[i]); 88262306a36Sopenharmony_ci if (requested_cycles == cycles) 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (i == ARRAY_SIZE(debounce_timers)) { 88762306a36Sopenharmony_ci int j; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* 89062306a36Sopenharmony_ci * As there are no timers configured for the requested debounce 89162306a36Sopenharmony_ci * period, find an unused timer instead 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_ci for (j = 1; j < ARRAY_SIZE(gpio->timer_users); j++) { 89462306a36Sopenharmony_ci if (gpio->timer_users[j] == 0) 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (j == ARRAY_SIZE(gpio->timer_users)) { 89962306a36Sopenharmony_ci dev_warn(chip->parent, 90062306a36Sopenharmony_ci "Debounce timers exhausted, cannot debounce for period %luus\n", 90162306a36Sopenharmony_ci usecs); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci rc = -EPERM; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* 90662306a36Sopenharmony_ci * We already adjusted the accounting to remove @offset 90762306a36Sopenharmony_ci * as a user of its previous timer, so also configure 90862306a36Sopenharmony_ci * the hardware so @offset has timers disabled for 90962306a36Sopenharmony_ci * consistency. 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_ci configure_timer(gpio, offset, 0); 91262306a36Sopenharmony_ci goto out; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci i = j; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci iowrite32(requested_cycles, gpio->base + debounce_timers[i]); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (WARN(i == 0, "Cannot register index of disabled timer\n")) { 92162306a36Sopenharmony_ci rc = -EINVAL; 92262306a36Sopenharmony_ci goto out; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci register_allocated_timer(gpio, offset, i); 92662306a36Sopenharmony_ci configure_timer(gpio, offset, i); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ciout: 92962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return rc; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int disable_debounce(struct gpio_chip *chip, unsigned int offset) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 93762306a36Sopenharmony_ci unsigned long flags; 93862306a36Sopenharmony_ci int rc; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci rc = unregister_allocated_timer(gpio, offset); 94362306a36Sopenharmony_ci if (!rc) 94462306a36Sopenharmony_ci configure_timer(gpio, offset, 0); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return rc; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int set_debounce(struct gpio_chip *chip, unsigned int offset, 95262306a36Sopenharmony_ci unsigned long usecs) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (!have_debounce(gpio, offset)) 95762306a36Sopenharmony_ci return -ENOTSUPP; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (usecs) 96062306a36Sopenharmony_ci return enable_debounce(chip, offset, usecs); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return disable_debounce(chip, offset); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, 96662306a36Sopenharmony_ci unsigned long config) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci unsigned long param = pinconf_to_config_param(config); 96962306a36Sopenharmony_ci u32 arg = pinconf_to_config_argument(config); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (param == PIN_CONFIG_INPUT_DEBOUNCE) 97262306a36Sopenharmony_ci return set_debounce(chip, offset, arg); 97362306a36Sopenharmony_ci else if (param == PIN_CONFIG_BIAS_DISABLE || 97462306a36Sopenharmony_ci param == PIN_CONFIG_BIAS_PULL_DOWN || 97562306a36Sopenharmony_ci param == PIN_CONFIG_DRIVE_STRENGTH) 97662306a36Sopenharmony_ci return pinctrl_gpio_set_config(chip->base + offset, config); 97762306a36Sopenharmony_ci else if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN || 97862306a36Sopenharmony_ci param == PIN_CONFIG_DRIVE_OPEN_SOURCE) 97962306a36Sopenharmony_ci /* Return -ENOTSUPP to trigger emulation, as per datasheet */ 98062306a36Sopenharmony_ci return -ENOTSUPP; 98162306a36Sopenharmony_ci else if (param == PIN_CONFIG_PERSIST_STATE) 98262306a36Sopenharmony_ci return aspeed_gpio_reset_tolerance(chip, offset, arg); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return -ENOTSUPP; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci/** 98862306a36Sopenharmony_ci * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with 98962306a36Sopenharmony_ci * the coprocessor for shared GPIO banks 99062306a36Sopenharmony_ci * @ops: The callbacks 99162306a36Sopenharmony_ci * @data: Pointer passed back to the callbacks 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ciint aspeed_gpio_copro_set_ops(const struct aspeed_gpio_copro_ops *ops, void *data) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci copro_data = data; 99662306a36Sopenharmony_ci copro_ops = ops; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci return 0; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(aspeed_gpio_copro_set_ops); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci/** 100362306a36Sopenharmony_ci * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire 100462306a36Sopenharmony_ci * bank gets marked and any access from the ARM will 100562306a36Sopenharmony_ci * result in handshaking via callbacks. 100662306a36Sopenharmony_ci * @desc: The GPIO to be marked 100762306a36Sopenharmony_ci * @vreg_offset: If non-NULL, returns the value register offset in the GPIO space 100862306a36Sopenharmony_ci * @dreg_offset: If non-NULL, returns the data latch register offset in the GPIO space 100962306a36Sopenharmony_ci * @bit: If non-NULL, returns the bit number of the GPIO in the registers 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ciint aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, 101262306a36Sopenharmony_ci u16 *vreg_offset, u16 *dreg_offset, u8 *bit) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci struct gpio_chip *chip = gpiod_to_chip(desc); 101562306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 101662306a36Sopenharmony_ci int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); 101762306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 101862306a36Sopenharmony_ci unsigned long flags; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (!gpio->cf_copro_bankmap) 102162306a36Sopenharmony_ci gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL); 102262306a36Sopenharmony_ci if (!gpio->cf_copro_bankmap) 102362306a36Sopenharmony_ci return -ENOMEM; 102462306a36Sopenharmony_ci if (offset < 0 || offset > gpio->chip.ngpio) 102562306a36Sopenharmony_ci return -EINVAL; 102662306a36Sopenharmony_ci bindex = offset >> 3; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* Sanity check, this shouldn't happen */ 103162306a36Sopenharmony_ci if (gpio->cf_copro_bankmap[bindex] == 0xff) { 103262306a36Sopenharmony_ci rc = -EIO; 103362306a36Sopenharmony_ci goto bail; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci gpio->cf_copro_bankmap[bindex]++; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* Switch command source */ 103862306a36Sopenharmony_ci if (gpio->cf_copro_bankmap[bindex] == 1) 103962306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, bindex, 104062306a36Sopenharmony_ci GPIO_CMDSRC_COLDFIRE); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (vreg_offset) 104362306a36Sopenharmony_ci *vreg_offset = bank->val_regs; 104462306a36Sopenharmony_ci if (dreg_offset) 104562306a36Sopenharmony_ci *dreg_offset = bank->rdata_reg; 104662306a36Sopenharmony_ci if (bit) 104762306a36Sopenharmony_ci *bit = GPIO_OFFSET(offset); 104862306a36Sopenharmony_ci bail: 104962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 105062306a36Sopenharmony_ci return rc; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/** 105562306a36Sopenharmony_ci * aspeed_gpio_copro_release_gpio - Unmark a GPIO used by the coprocessor. 105662306a36Sopenharmony_ci * @desc: The GPIO to be marked 105762306a36Sopenharmony_ci */ 105862306a36Sopenharmony_ciint aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct gpio_chip *chip = gpiod_to_chip(desc); 106162306a36Sopenharmony_ci struct aspeed_gpio *gpio = gpiochip_get_data(chip); 106262306a36Sopenharmony_ci int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); 106362306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = to_bank(offset); 106462306a36Sopenharmony_ci unsigned long flags; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (!gpio->cf_copro_bankmap) 106762306a36Sopenharmony_ci return -ENXIO; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (offset < 0 || offset > gpio->chip.ngpio) 107062306a36Sopenharmony_ci return -EINVAL; 107162306a36Sopenharmony_ci bindex = offset >> 3; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpio->lock, flags); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* Sanity check, this shouldn't happen */ 107662306a36Sopenharmony_ci if (gpio->cf_copro_bankmap[bindex] == 0) { 107762306a36Sopenharmony_ci rc = -EIO; 107862306a36Sopenharmony_ci goto bail; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci gpio->cf_copro_bankmap[bindex]--; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* Switch command source */ 108362306a36Sopenharmony_ci if (gpio->cf_copro_bankmap[bindex] == 0) 108462306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, bindex, 108562306a36Sopenharmony_ci GPIO_CMDSRC_ARM); 108662306a36Sopenharmony_ci bail: 108762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpio->lock, flags); 108862306a36Sopenharmony_ci return rc; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic void aspeed_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank; 109562306a36Sopenharmony_ci struct aspeed_gpio *gpio; 109662306a36Sopenharmony_ci u32 bit; 109762306a36Sopenharmony_ci int rc, offset; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); 110062306a36Sopenharmony_ci if (rc) 110162306a36Sopenharmony_ci return; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci seq_printf(p, dev_name(gpio->dev)); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic const struct irq_chip aspeed_gpio_irq_chip = { 110762306a36Sopenharmony_ci .irq_ack = aspeed_gpio_irq_ack, 110862306a36Sopenharmony_ci .irq_mask = aspeed_gpio_irq_mask, 110962306a36Sopenharmony_ci .irq_unmask = aspeed_gpio_irq_unmask, 111062306a36Sopenharmony_ci .irq_set_type = aspeed_gpio_set_type, 111162306a36Sopenharmony_ci .irq_print_chip = aspeed_gpio_irq_print_chip, 111262306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 111362306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 111462306a36Sopenharmony_ci}; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci/* 111762306a36Sopenharmony_ci * Any banks not specified in a struct aspeed_bank_props array are assumed to 111862306a36Sopenharmony_ci * have the properties: 111962306a36Sopenharmony_ci * 112062306a36Sopenharmony_ci * { .input = 0xffffffff, .output = 0xffffffff } 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic const struct aspeed_bank_props ast2400_bank_props[] = { 112462306a36Sopenharmony_ci /* input output */ 112562306a36Sopenharmony_ci { 5, 0xffffffff, 0x0000ffff }, /* U/V/W/X */ 112662306a36Sopenharmony_ci { 6, 0x0000000f, 0x0fffff0f }, /* Y/Z/AA/AB, two 4-GPIO holes */ 112762306a36Sopenharmony_ci { }, 112862306a36Sopenharmony_ci}; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic const struct aspeed_gpio_config ast2400_config = 113162306a36Sopenharmony_ci /* 220 for simplicity, really 216 with two 4-GPIO holes, four at end */ 113262306a36Sopenharmony_ci { .nr_gpios = 220, .props = ast2400_bank_props, }; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic const struct aspeed_bank_props ast2500_bank_props[] = { 113562306a36Sopenharmony_ci /* input output */ 113662306a36Sopenharmony_ci { 5, 0xffffffff, 0x0000ffff }, /* U/V/W/X */ 113762306a36Sopenharmony_ci { 6, 0x0fffffff, 0x0fffffff }, /* Y/Z/AA/AB, 4-GPIO hole */ 113862306a36Sopenharmony_ci { 7, 0x000000ff, 0x000000ff }, /* AC */ 113962306a36Sopenharmony_ci { }, 114062306a36Sopenharmony_ci}; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic const struct aspeed_gpio_config ast2500_config = 114362306a36Sopenharmony_ci /* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */ 114462306a36Sopenharmony_ci { .nr_gpios = 232, .props = ast2500_bank_props, }; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic const struct aspeed_bank_props ast2600_bank_props[] = { 114762306a36Sopenharmony_ci /* input output */ 114862306a36Sopenharmony_ci {4, 0xffffffff, 0x00ffffff}, /* Q/R/S/T */ 114962306a36Sopenharmony_ci {5, 0xffffffff, 0xffffff00}, /* U/V/W/X */ 115062306a36Sopenharmony_ci {6, 0x0000ffff, 0x0000ffff}, /* Y/Z */ 115162306a36Sopenharmony_ci { }, 115262306a36Sopenharmony_ci}; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic const struct aspeed_gpio_config ast2600_config = 115562306a36Sopenharmony_ci /* 115662306a36Sopenharmony_ci * ast2600 has two controllers one with 208 GPIOs and one with 36 GPIOs. 115762306a36Sopenharmony_ci * We expect ngpio being set in the device tree and this is a fallback 115862306a36Sopenharmony_ci * option. 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ci { .nr_gpios = 208, .props = ast2600_bank_props, }; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic const struct of_device_id aspeed_gpio_of_table[] = { 116362306a36Sopenharmony_ci { .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, }, 116462306a36Sopenharmony_ci { .compatible = "aspeed,ast2500-gpio", .data = &ast2500_config, }, 116562306a36Sopenharmony_ci { .compatible = "aspeed,ast2600-gpio", .data = &ast2600_config, }, 116662306a36Sopenharmony_ci {} 116762306a36Sopenharmony_ci}; 116862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic int __init aspeed_gpio_probe(struct platform_device *pdev) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci const struct of_device_id *gpio_id; 117362306a36Sopenharmony_ci struct gpio_irq_chip *girq; 117462306a36Sopenharmony_ci struct aspeed_gpio *gpio; 117562306a36Sopenharmony_ci int rc, irq, i, banks, err; 117662306a36Sopenharmony_ci u32 ngpio; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); 117962306a36Sopenharmony_ci if (!gpio) 118062306a36Sopenharmony_ci return -ENOMEM; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci gpio->base = devm_platform_ioremap_resource(pdev, 0); 118362306a36Sopenharmony_ci if (IS_ERR(gpio->base)) 118462306a36Sopenharmony_ci return PTR_ERR(gpio->base); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci gpio->dev = &pdev->dev; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci raw_spin_lock_init(&gpio->lock); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); 119162306a36Sopenharmony_ci if (!gpio_id) 119262306a36Sopenharmony_ci return -EINVAL; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci gpio->clk = of_clk_get(pdev->dev.of_node, 0); 119562306a36Sopenharmony_ci if (IS_ERR(gpio->clk)) { 119662306a36Sopenharmony_ci dev_warn(&pdev->dev, 119762306a36Sopenharmony_ci "Failed to get clock from devicetree, debouncing disabled\n"); 119862306a36Sopenharmony_ci gpio->clk = NULL; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci gpio->config = gpio_id->data; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci gpio->chip.parent = &pdev->dev; 120462306a36Sopenharmony_ci err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio); 120562306a36Sopenharmony_ci gpio->chip.ngpio = (u16) ngpio; 120662306a36Sopenharmony_ci if (err) 120762306a36Sopenharmony_ci gpio->chip.ngpio = gpio->config->nr_gpios; 120862306a36Sopenharmony_ci gpio->chip.direction_input = aspeed_gpio_dir_in; 120962306a36Sopenharmony_ci gpio->chip.direction_output = aspeed_gpio_dir_out; 121062306a36Sopenharmony_ci gpio->chip.get_direction = aspeed_gpio_get_direction; 121162306a36Sopenharmony_ci gpio->chip.request = aspeed_gpio_request; 121262306a36Sopenharmony_ci gpio->chip.free = aspeed_gpio_free; 121362306a36Sopenharmony_ci gpio->chip.get = aspeed_gpio_get; 121462306a36Sopenharmony_ci gpio->chip.set = aspeed_gpio_set; 121562306a36Sopenharmony_ci gpio->chip.set_config = aspeed_gpio_set_config; 121662306a36Sopenharmony_ci gpio->chip.label = dev_name(&pdev->dev); 121762306a36Sopenharmony_ci gpio->chip.base = -1; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Allocate a cache of the output registers */ 122062306a36Sopenharmony_ci banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); 122162306a36Sopenharmony_ci gpio->dcache = devm_kcalloc(&pdev->dev, 122262306a36Sopenharmony_ci banks, sizeof(u32), GFP_KERNEL); 122362306a36Sopenharmony_ci if (!gpio->dcache) 122462306a36Sopenharmony_ci return -ENOMEM; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* 122762306a36Sopenharmony_ci * Populate it with initial values read from the HW and switch 122862306a36Sopenharmony_ci * all command sources to the ARM by default 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci for (i = 0; i < banks; i++) { 123162306a36Sopenharmony_ci const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; 123262306a36Sopenharmony_ci void __iomem *addr = bank_reg(gpio, bank, reg_rdata); 123362306a36Sopenharmony_ci gpio->dcache[i] = ioread32(addr); 123462306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, 0, GPIO_CMDSRC_ARM); 123562306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, 1, GPIO_CMDSRC_ARM); 123662306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, 2, GPIO_CMDSRC_ARM); 123762306a36Sopenharmony_ci aspeed_gpio_change_cmd_source(gpio, bank, 3, GPIO_CMDSRC_ARM); 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* Set up an irqchip */ 124162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 124262306a36Sopenharmony_ci if (irq < 0) 124362306a36Sopenharmony_ci return irq; 124462306a36Sopenharmony_ci gpio->irq = irq; 124562306a36Sopenharmony_ci girq = &gpio->chip.irq; 124662306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &aspeed_gpio_irq_chip); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci girq->parent_handler = aspeed_gpio_irq_handler; 124962306a36Sopenharmony_ci girq->num_parents = 1; 125062306a36Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), GFP_KERNEL); 125162306a36Sopenharmony_ci if (!girq->parents) 125262306a36Sopenharmony_ci return -ENOMEM; 125362306a36Sopenharmony_ci girq->parents[0] = gpio->irq; 125462306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 125562306a36Sopenharmony_ci girq->handler = handle_bad_irq; 125662306a36Sopenharmony_ci girq->init_valid_mask = aspeed_init_irq_valid_mask; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci gpio->offset_timer = 125962306a36Sopenharmony_ci devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL); 126062306a36Sopenharmony_ci if (!gpio->offset_timer) 126162306a36Sopenharmony_ci return -ENOMEM; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); 126462306a36Sopenharmony_ci if (rc < 0) 126562306a36Sopenharmony_ci return rc; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci return 0; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic struct platform_driver aspeed_gpio_driver = { 127162306a36Sopenharmony_ci .driver = { 127262306a36Sopenharmony_ci .name = KBUILD_MODNAME, 127362306a36Sopenharmony_ci .of_match_table = aspeed_gpio_of_table, 127462306a36Sopenharmony_ci }, 127562306a36Sopenharmony_ci}; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cimodule_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ciMODULE_DESCRIPTION("Aspeed GPIO Driver"); 128062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1281