18b37eb74SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0 2119f5e44SMagnus Damm /* 3119f5e44SMagnus Damm * Renesas R-Car GPIO Support 4119f5e44SMagnus Damm * 51fd2b49dSHisashi Nakamura * Copyright (C) 2014 Renesas Electronics Corporation 6119f5e44SMagnus Damm * Copyright (C) 2013 Magnus Damm 7119f5e44SMagnus Damm */ 8119f5e44SMagnus Damm 9119f5e44SMagnus Damm #include <linux/err.h> 104b1d8007SLinus Walleij #include <linux/gpio/driver.h> 11119f5e44SMagnus Damm #include <linux/init.h> 12119f5e44SMagnus Damm #include <linux/interrupt.h> 13119f5e44SMagnus Damm #include <linux/io.h> 14119f5e44SMagnus Damm #include <linux/ioport.h> 15119f5e44SMagnus Damm #include <linux/irq.h> 16119f5e44SMagnus Damm #include <linux/module.h> 17bd0bf468SSachin Kamat #include <linux/of.h> 18f9f2a6feSGeert Uytterhoeven #include <linux/of_device.h> 19dc3465a9SLaurent Pinchart #include <linux/pinctrl/consumer.h> 20119f5e44SMagnus Damm #include <linux/platform_device.h> 21df0c6c80SGeert Uytterhoeven #include <linux/pm_runtime.h> 22119f5e44SMagnus Damm #include <linux/spinlock.h> 23119f5e44SMagnus Damm #include <linux/slab.h> 24119f5e44SMagnus Damm 2551750fb1SHien Dang struct gpio_rcar_bank_info { 2651750fb1SHien Dang u32 iointsel; 2751750fb1SHien Dang u32 inoutsel; 2851750fb1SHien Dang u32 outdt; 2951750fb1SHien Dang u32 posneg; 3051750fb1SHien Dang u32 edglevel; 3151750fb1SHien Dang u32 bothedge; 3251750fb1SHien Dang u32 intmsk; 3351750fb1SHien Dang }; 3451750fb1SHien Dang 35119f5e44SMagnus Damm struct gpio_rcar_priv { 36119f5e44SMagnus Damm void __iomem *base; 37119f5e44SMagnus Damm spinlock_t lock; 38a53f7953SVladimir Zapolskiy struct device *dev; 39119f5e44SMagnus Damm struct gpio_chip gpio_chip; 40119f5e44SMagnus Damm struct irq_chip irq_chip; 418b092be9SGeert Uytterhoeven unsigned int irq_parent; 429ac79ba9SGeert Uytterhoeven atomic_t wakeup_path; 438b092be9SGeert Uytterhoeven bool has_both_edge_trigger; 4451750fb1SHien Dang struct gpio_rcar_bank_info bank_info; 45119f5e44SMagnus Damm }; 46119f5e44SMagnus Damm 473dc1e685SGeert Uytterhoeven #define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ 483dc1e685SGeert Uytterhoeven #define INOUTSEL 0x04 /* General Input/Output Switching Register */ 493dc1e685SGeert Uytterhoeven #define OUTDT 0x08 /* General Output Register */ 503dc1e685SGeert Uytterhoeven #define INDT 0x0c /* General Input Register */ 513dc1e685SGeert Uytterhoeven #define INTDT 0x10 /* Interrupt Display Register */ 523dc1e685SGeert Uytterhoeven #define INTCLR 0x14 /* Interrupt Clear Register */ 533dc1e685SGeert Uytterhoeven #define INTMSK 0x18 /* Interrupt Mask Register */ 543dc1e685SGeert Uytterhoeven #define MSKCLR 0x1c /* Interrupt Mask Clear Register */ 553dc1e685SGeert Uytterhoeven #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ 563dc1e685SGeert Uytterhoeven #define EDGLEVEL 0x24 /* Edge/level Select Register */ 573dc1e685SGeert Uytterhoeven #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ 583dc1e685SGeert Uytterhoeven #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ 59119f5e44SMagnus Damm 60159f8a02SLaurent Pinchart #define RCAR_MAX_GPIO_PER_BANK 32 61159f8a02SLaurent Pinchart 62119f5e44SMagnus Damm static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) 63119f5e44SMagnus Damm { 64119f5e44SMagnus Damm return ioread32(p->base + offs); 65119f5e44SMagnus Damm } 66119f5e44SMagnus Damm 67119f5e44SMagnus Damm static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs, 68119f5e44SMagnus Damm u32 value) 69119f5e44SMagnus Damm { 70119f5e44SMagnus Damm iowrite32(value, p->base + offs); 71119f5e44SMagnus Damm } 72119f5e44SMagnus Damm 73119f5e44SMagnus Damm static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs, 74119f5e44SMagnus Damm int bit, bool value) 75119f5e44SMagnus Damm { 76119f5e44SMagnus Damm u32 tmp = gpio_rcar_read(p, offs); 77119f5e44SMagnus Damm 78119f5e44SMagnus Damm if (value) 79119f5e44SMagnus Damm tmp |= BIT(bit); 80119f5e44SMagnus Damm else 81119f5e44SMagnus Damm tmp &= ~BIT(bit); 82119f5e44SMagnus Damm 83119f5e44SMagnus Damm gpio_rcar_write(p, offs, tmp); 84119f5e44SMagnus Damm } 85119f5e44SMagnus Damm 86119f5e44SMagnus Damm static void gpio_rcar_irq_disable(struct irq_data *d) 87119f5e44SMagnus Damm { 88c7f3c5d3SGeert Uytterhoeven struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 89c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(gc); 90119f5e44SMagnus Damm 91119f5e44SMagnus Damm gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d))); 92119f5e44SMagnus Damm } 93119f5e44SMagnus Damm 94119f5e44SMagnus Damm static void gpio_rcar_irq_enable(struct irq_data *d) 95119f5e44SMagnus Damm { 96c7f3c5d3SGeert Uytterhoeven struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 97c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(gc); 98119f5e44SMagnus Damm 99119f5e44SMagnus Damm gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d))); 100119f5e44SMagnus Damm } 101119f5e44SMagnus Damm 102119f5e44SMagnus Damm static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, 103119f5e44SMagnus Damm unsigned int hwirq, 104119f5e44SMagnus Damm bool active_high_rising_edge, 1057e1092b5SSimon Horman bool level_trigger, 1067e1092b5SSimon Horman bool both) 107119f5e44SMagnus Damm { 108119f5e44SMagnus Damm unsigned long flags; 109119f5e44SMagnus Damm 110119f5e44SMagnus Damm /* follow steps in the GPIO documentation for 111119f5e44SMagnus Damm * "Setting Edge-Sensitive Interrupt Input Mode" and 112119f5e44SMagnus Damm * "Setting Level-Sensitive Interrupt Input Mode" 113119f5e44SMagnus Damm */ 114119f5e44SMagnus Damm 115119f5e44SMagnus Damm spin_lock_irqsave(&p->lock, flags); 116119f5e44SMagnus Damm 117119f5e44SMagnus Damm /* Configure postive or negative logic in POSNEG */ 118119f5e44SMagnus Damm gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge); 119119f5e44SMagnus Damm 120119f5e44SMagnus Damm /* Configure edge or level trigger in EDGLEVEL */ 121119f5e44SMagnus Damm gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); 122119f5e44SMagnus Damm 1237e1092b5SSimon Horman /* Select one edge or both edges in BOTHEDGE */ 1248b092be9SGeert Uytterhoeven if (p->has_both_edge_trigger) 1257e1092b5SSimon Horman gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); 1267e1092b5SSimon Horman 127119f5e44SMagnus Damm /* Select "Interrupt Input Mode" in IOINTSEL */ 128119f5e44SMagnus Damm gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); 129119f5e44SMagnus Damm 130119f5e44SMagnus Damm /* Write INTCLR in case of edge trigger */ 131119f5e44SMagnus Damm if (!level_trigger) 132119f5e44SMagnus Damm gpio_rcar_write(p, INTCLR, BIT(hwirq)); 133119f5e44SMagnus Damm 134119f5e44SMagnus Damm spin_unlock_irqrestore(&p->lock, flags); 135119f5e44SMagnus Damm } 136119f5e44SMagnus Damm 137119f5e44SMagnus Damm static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) 138119f5e44SMagnus Damm { 139c7f3c5d3SGeert Uytterhoeven struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 140c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(gc); 141119f5e44SMagnus Damm unsigned int hwirq = irqd_to_hwirq(d); 142119f5e44SMagnus Damm 143a53f7953SVladimir Zapolskiy dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type); 144119f5e44SMagnus Damm 145119f5e44SMagnus Damm switch (type & IRQ_TYPE_SENSE_MASK) { 146119f5e44SMagnus Damm case IRQ_TYPE_LEVEL_HIGH: 1477e1092b5SSimon Horman gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, 1487e1092b5SSimon Horman false); 149119f5e44SMagnus Damm break; 150119f5e44SMagnus Damm case IRQ_TYPE_LEVEL_LOW: 1517e1092b5SSimon Horman gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, 1527e1092b5SSimon Horman false); 153119f5e44SMagnus Damm break; 154119f5e44SMagnus Damm case IRQ_TYPE_EDGE_RISING: 1557e1092b5SSimon Horman gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, 1567e1092b5SSimon Horman false); 157119f5e44SMagnus Damm break; 158119f5e44SMagnus Damm case IRQ_TYPE_EDGE_FALLING: 1597e1092b5SSimon Horman gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, 1607e1092b5SSimon Horman false); 1617e1092b5SSimon Horman break; 1627e1092b5SSimon Horman case IRQ_TYPE_EDGE_BOTH: 1638b092be9SGeert Uytterhoeven if (!p->has_both_edge_trigger) 1647e1092b5SSimon Horman return -EINVAL; 1657e1092b5SSimon Horman gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, 1667e1092b5SSimon Horman true); 167119f5e44SMagnus Damm break; 168119f5e44SMagnus Damm default: 169119f5e44SMagnus Damm return -EINVAL; 170119f5e44SMagnus Damm } 171119f5e44SMagnus Damm return 0; 172119f5e44SMagnus Damm } 173119f5e44SMagnus Damm 174ab82fa7dSGeert Uytterhoeven static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on) 175ab82fa7dSGeert Uytterhoeven { 176ab82fa7dSGeert Uytterhoeven struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 177c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(gc); 178501ef0f9SGeert Uytterhoeven int error; 179ab82fa7dSGeert Uytterhoeven 180501ef0f9SGeert Uytterhoeven if (p->irq_parent) { 181501ef0f9SGeert Uytterhoeven error = irq_set_irq_wake(p->irq_parent, on); 182501ef0f9SGeert Uytterhoeven if (error) { 183a53f7953SVladimir Zapolskiy dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n", 184501ef0f9SGeert Uytterhoeven p->irq_parent); 185501ef0f9SGeert Uytterhoeven p->irq_parent = 0; 186501ef0f9SGeert Uytterhoeven } 187501ef0f9SGeert Uytterhoeven } 188ab82fa7dSGeert Uytterhoeven 189ab82fa7dSGeert Uytterhoeven if (on) 1909ac79ba9SGeert Uytterhoeven atomic_inc(&p->wakeup_path); 191ab82fa7dSGeert Uytterhoeven else 1929ac79ba9SGeert Uytterhoeven atomic_dec(&p->wakeup_path); 193ab82fa7dSGeert Uytterhoeven 194ab82fa7dSGeert Uytterhoeven return 0; 195ab82fa7dSGeert Uytterhoeven } 196ab82fa7dSGeert Uytterhoeven 197119f5e44SMagnus Damm static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id) 198119f5e44SMagnus Damm { 199119f5e44SMagnus Damm struct gpio_rcar_priv *p = dev_id; 200119f5e44SMagnus Damm u32 pending; 201119f5e44SMagnus Damm unsigned int offset, irqs_handled = 0; 202119f5e44SMagnus Damm 2038808b64dSValentine Barshak while ((pending = gpio_rcar_read(p, INTDT) & 2048808b64dSValentine Barshak gpio_rcar_read(p, INTMSK))) { 205119f5e44SMagnus Damm offset = __ffs(pending); 206119f5e44SMagnus Damm gpio_rcar_write(p, INTCLR, BIT(offset)); 207f0fbe7bcSThierry Reding generic_handle_irq(irq_find_mapping(p->gpio_chip.irq.domain, 208c7f3c5d3SGeert Uytterhoeven offset)); 209119f5e44SMagnus Damm irqs_handled++; 210119f5e44SMagnus Damm } 211119f5e44SMagnus Damm 212119f5e44SMagnus Damm return irqs_handled ? IRQ_HANDLED : IRQ_NONE; 213119f5e44SMagnus Damm } 214119f5e44SMagnus Damm 215119f5e44SMagnus Damm static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, 216119f5e44SMagnus Damm unsigned int gpio, 217119f5e44SMagnus Damm bool output) 218119f5e44SMagnus Damm { 219c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(chip); 220119f5e44SMagnus Damm unsigned long flags; 221119f5e44SMagnus Damm 222119f5e44SMagnus Damm /* follow steps in the GPIO documentation for 223119f5e44SMagnus Damm * "Setting General Output Mode" and 224119f5e44SMagnus Damm * "Setting General Input Mode" 225119f5e44SMagnus Damm */ 226119f5e44SMagnus Damm 227119f5e44SMagnus Damm spin_lock_irqsave(&p->lock, flags); 228119f5e44SMagnus Damm 229119f5e44SMagnus Damm /* Configure postive logic in POSNEG */ 230119f5e44SMagnus Damm gpio_rcar_modify_bit(p, POSNEG, gpio, false); 231119f5e44SMagnus Damm 232119f5e44SMagnus Damm /* Select "General Input/Output Mode" in IOINTSEL */ 233119f5e44SMagnus Damm gpio_rcar_modify_bit(p, IOINTSEL, gpio, false); 234119f5e44SMagnus Damm 235119f5e44SMagnus Damm /* Select Input Mode or Output Mode in INOUTSEL */ 236119f5e44SMagnus Damm gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); 237119f5e44SMagnus Damm 238119f5e44SMagnus Damm spin_unlock_irqrestore(&p->lock, flags); 239119f5e44SMagnus Damm } 240119f5e44SMagnus Damm 241dc3465a9SLaurent Pinchart static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) 242dc3465a9SLaurent Pinchart { 2432d65472bSGeert Uytterhoeven struct gpio_rcar_priv *p = gpiochip_get_data(chip); 2442d65472bSGeert Uytterhoeven int error; 2452d65472bSGeert Uytterhoeven 246a53f7953SVladimir Zapolskiy error = pm_runtime_get_sync(p->dev); 2472d65472bSGeert Uytterhoeven if (error < 0) 2482d65472bSGeert Uytterhoeven return error; 2492d65472bSGeert Uytterhoeven 250a9a1d2a7SLinus Walleij error = pinctrl_gpio_request(chip->base + offset); 2512d65472bSGeert Uytterhoeven if (error) 252a53f7953SVladimir Zapolskiy pm_runtime_put(p->dev); 2532d65472bSGeert Uytterhoeven 2542d65472bSGeert Uytterhoeven return error; 255dc3465a9SLaurent Pinchart } 256dc3465a9SLaurent Pinchart 257dc3465a9SLaurent Pinchart static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) 258dc3465a9SLaurent Pinchart { 2592d65472bSGeert Uytterhoeven struct gpio_rcar_priv *p = gpiochip_get_data(chip); 2602d65472bSGeert Uytterhoeven 261a9a1d2a7SLinus Walleij pinctrl_gpio_free(chip->base + offset); 262dc3465a9SLaurent Pinchart 263ce0e2c60SLinus Walleij /* 264ce0e2c60SLinus Walleij * Set the GPIO as an input to ensure that the next GPIO request won't 265dc3465a9SLaurent Pinchart * drive the GPIO pin as an output. 266dc3465a9SLaurent Pinchart */ 267dc3465a9SLaurent Pinchart gpio_rcar_config_general_input_output_mode(chip, offset, false); 2682d65472bSGeert Uytterhoeven 269a53f7953SVladimir Zapolskiy pm_runtime_put(p->dev); 270dc3465a9SLaurent Pinchart } 271dc3465a9SLaurent Pinchart 272ad817297SGeert Uytterhoeven static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset) 273ad817297SGeert Uytterhoeven { 274ad817297SGeert Uytterhoeven struct gpio_rcar_priv *p = gpiochip_get_data(chip); 275ad817297SGeert Uytterhoeven 276ad817297SGeert Uytterhoeven return !(gpio_rcar_read(p, INOUTSEL) & BIT(offset)); 277ad817297SGeert Uytterhoeven } 278ad817297SGeert Uytterhoeven 279119f5e44SMagnus Damm static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) 280119f5e44SMagnus Damm { 281119f5e44SMagnus Damm gpio_rcar_config_general_input_output_mode(chip, offset, false); 282119f5e44SMagnus Damm return 0; 283119f5e44SMagnus Damm } 284119f5e44SMagnus Damm 285119f5e44SMagnus Damm static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) 286119f5e44SMagnus Damm { 287ae9550f6SMagnus Damm u32 bit = BIT(offset); 288ae9550f6SMagnus Damm 289ae9550f6SMagnus Damm /* testing on r8a7790 shows that INDT does not show correct pin state 290ae9550f6SMagnus Damm * when configured as output, so use OUTDT in case of output pins */ 291c7b6f457SLinus Walleij if (gpio_rcar_read(gpiochip_get_data(chip), INOUTSEL) & bit) 292c7b6f457SLinus Walleij return !!(gpio_rcar_read(gpiochip_get_data(chip), OUTDT) & bit); 293ae9550f6SMagnus Damm else 294c7b6f457SLinus Walleij return !!(gpio_rcar_read(gpiochip_get_data(chip), INDT) & bit); 295119f5e44SMagnus Damm } 296119f5e44SMagnus Damm 297119f5e44SMagnus Damm static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) 298119f5e44SMagnus Damm { 299c7b6f457SLinus Walleij struct gpio_rcar_priv *p = gpiochip_get_data(chip); 300119f5e44SMagnus Damm unsigned long flags; 301119f5e44SMagnus Damm 302119f5e44SMagnus Damm spin_lock_irqsave(&p->lock, flags); 303119f5e44SMagnus Damm gpio_rcar_modify_bit(p, OUTDT, offset, value); 304119f5e44SMagnus Damm spin_unlock_irqrestore(&p->lock, flags); 305119f5e44SMagnus Damm } 306119f5e44SMagnus Damm 307dbb763b8SGeert Uytterhoeven static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, 308dbb763b8SGeert Uytterhoeven unsigned long *bits) 309dbb763b8SGeert Uytterhoeven { 310dbb763b8SGeert Uytterhoeven struct gpio_rcar_priv *p = gpiochip_get_data(chip); 311dbb763b8SGeert Uytterhoeven unsigned long flags; 312dbb763b8SGeert Uytterhoeven u32 val, bankmask; 313dbb763b8SGeert Uytterhoeven 314dbb763b8SGeert Uytterhoeven bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); 315496069b8SBiju Das if (chip->valid_mask) 316496069b8SBiju Das bankmask &= chip->valid_mask[0]; 317496069b8SBiju Das 318dbb763b8SGeert Uytterhoeven if (!bankmask) 319dbb763b8SGeert Uytterhoeven return; 320dbb763b8SGeert Uytterhoeven 321dbb763b8SGeert Uytterhoeven spin_lock_irqsave(&p->lock, flags); 322dbb763b8SGeert Uytterhoeven val = gpio_rcar_read(p, OUTDT); 323dbb763b8SGeert Uytterhoeven val &= ~bankmask; 324dbb763b8SGeert Uytterhoeven val |= (bankmask & bits[0]); 325dbb763b8SGeert Uytterhoeven gpio_rcar_write(p, OUTDT, val); 326dbb763b8SGeert Uytterhoeven spin_unlock_irqrestore(&p->lock, flags); 327dbb763b8SGeert Uytterhoeven } 328dbb763b8SGeert Uytterhoeven 329119f5e44SMagnus Damm static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, 330119f5e44SMagnus Damm int value) 331119f5e44SMagnus Damm { 332119f5e44SMagnus Damm /* write GPIO value to output before selecting output mode of pin */ 333119f5e44SMagnus Damm gpio_rcar_set(chip, offset, value); 334119f5e44SMagnus Damm gpio_rcar_config_general_input_output_mode(chip, offset, true); 335119f5e44SMagnus Damm return 0; 336119f5e44SMagnus Damm } 337119f5e44SMagnus Damm 338850dfe17SLaurent Pinchart struct gpio_rcar_info { 339850dfe17SLaurent Pinchart bool has_both_edge_trigger; 340850dfe17SLaurent Pinchart }; 341850dfe17SLaurent Pinchart 3421fd2b49dSHisashi Nakamura static const struct gpio_rcar_info gpio_rcar_info_gen1 = { 3431fd2b49dSHisashi Nakamura .has_both_edge_trigger = false, 3441fd2b49dSHisashi Nakamura }; 3451fd2b49dSHisashi Nakamura 3461fd2b49dSHisashi Nakamura static const struct gpio_rcar_info gpio_rcar_info_gen2 = { 3471fd2b49dSHisashi Nakamura .has_both_edge_trigger = true, 3481fd2b49dSHisashi Nakamura }; 3491fd2b49dSHisashi Nakamura 350850dfe17SLaurent Pinchart static const struct of_device_id gpio_rcar_of_table[] = { 351850dfe17SLaurent Pinchart { 35285bb4646SBiju Das .compatible = "renesas,gpio-r8a7743", 35385bb4646SBiju Das /* RZ/G1 GPIO is identical to R-Car Gen2. */ 35485bb4646SBiju Das .data = &gpio_rcar_info_gen2, 35585bb4646SBiju Das }, { 356850dfe17SLaurent Pinchart .compatible = "renesas,gpio-r8a7790", 3571fd2b49dSHisashi Nakamura .data = &gpio_rcar_info_gen2, 358850dfe17SLaurent Pinchart }, { 359850dfe17SLaurent Pinchart .compatible = "renesas,gpio-r8a7791", 3601fd2b49dSHisashi Nakamura .data = &gpio_rcar_info_gen2, 3611fd2b49dSHisashi Nakamura }, { 362e79c5830SSergei Shtylyov .compatible = "renesas,gpio-r8a7792", 363e79c5830SSergei Shtylyov .data = &gpio_rcar_info_gen2, 364e79c5830SSergei Shtylyov }, { 3651fd2b49dSHisashi Nakamura .compatible = "renesas,gpio-r8a7793", 3661fd2b49dSHisashi Nakamura .data = &gpio_rcar_info_gen2, 3671fd2b49dSHisashi Nakamura }, { 3681fd2b49dSHisashi Nakamura .compatible = "renesas,gpio-r8a7794", 3691fd2b49dSHisashi Nakamura .data = &gpio_rcar_info_gen2, 370850dfe17SLaurent Pinchart }, { 3718cd14702SUlrich Hecht .compatible = "renesas,gpio-r8a7795", 3728cd14702SUlrich Hecht /* Gen3 GPIO is identical to Gen2. */ 3738cd14702SUlrich Hecht .data = &gpio_rcar_info_gen2, 3748cd14702SUlrich Hecht }, { 3755d2f1d6eSSimon Horman .compatible = "renesas,gpio-r8a7796", 3765d2f1d6eSSimon Horman /* Gen3 GPIO is identical to Gen2. */ 3775d2f1d6eSSimon Horman .data = &gpio_rcar_info_gen2, 3785d2f1d6eSSimon Horman }, { 379dbd1dad2SSimon Horman .compatible = "renesas,rcar-gen1-gpio", 380dbd1dad2SSimon Horman .data = &gpio_rcar_info_gen1, 381dbd1dad2SSimon Horman }, { 382dbd1dad2SSimon Horman .compatible = "renesas,rcar-gen2-gpio", 383dbd1dad2SSimon Horman .data = &gpio_rcar_info_gen2, 384dbd1dad2SSimon Horman }, { 385dbd1dad2SSimon Horman .compatible = "renesas,rcar-gen3-gpio", 386dbd1dad2SSimon Horman /* Gen3 GPIO is identical to Gen2. */ 387dbd1dad2SSimon Horman .data = &gpio_rcar_info_gen2, 388dbd1dad2SSimon Horman }, { 389850dfe17SLaurent Pinchart .compatible = "renesas,gpio-rcar", 3901fd2b49dSHisashi Nakamura .data = &gpio_rcar_info_gen1, 391850dfe17SLaurent Pinchart }, { 392850dfe17SLaurent Pinchart /* Terminator */ 393850dfe17SLaurent Pinchart }, 394850dfe17SLaurent Pinchart }; 395850dfe17SLaurent Pinchart 396850dfe17SLaurent Pinchart MODULE_DEVICE_TABLE(of, gpio_rcar_of_table); 397850dfe17SLaurent Pinchart 3988b092be9SGeert Uytterhoeven static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) 399159f8a02SLaurent Pinchart { 400a53f7953SVladimir Zapolskiy struct device_node *np = p->dev->of_node; 401850dfe17SLaurent Pinchart const struct gpio_rcar_info *info; 4028b092be9SGeert Uytterhoeven struct of_phandle_args args; 4038b092be9SGeert Uytterhoeven int ret; 404850dfe17SLaurent Pinchart 405a53f7953SVladimir Zapolskiy info = of_device_get_match_data(p->dev); 406850dfe17SLaurent Pinchart 4078b092be9SGeert Uytterhoeven ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); 4088b092be9SGeert Uytterhoeven *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; 4098b092be9SGeert Uytterhoeven p->has_both_edge_trigger = info->has_both_edge_trigger; 410159f8a02SLaurent Pinchart 4118b092be9SGeert Uytterhoeven if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { 412a53f7953SVladimir Zapolskiy dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", 413a53f7953SVladimir Zapolskiy *npins, RCAR_MAX_GPIO_PER_BANK); 4148b092be9SGeert Uytterhoeven *npins = RCAR_MAX_GPIO_PER_BANK; 415159f8a02SLaurent Pinchart } 416850dfe17SLaurent Pinchart 417850dfe17SLaurent Pinchart return 0; 418159f8a02SLaurent Pinchart } 419159f8a02SLaurent Pinchart 420119f5e44SMagnus Damm static int gpio_rcar_probe(struct platform_device *pdev) 421119f5e44SMagnus Damm { 422119f5e44SMagnus Damm struct gpio_rcar_priv *p; 423119f5e44SMagnus Damm struct resource *io, *irq; 424119f5e44SMagnus Damm struct gpio_chip *gpio_chip; 425119f5e44SMagnus Damm struct irq_chip *irq_chip; 426b22978fcSGeert Uytterhoeven struct device *dev = &pdev->dev; 427b22978fcSGeert Uytterhoeven const char *name = dev_name(dev); 4288b092be9SGeert Uytterhoeven unsigned int npins; 429119f5e44SMagnus Damm int ret; 430119f5e44SMagnus Damm 431b22978fcSGeert Uytterhoeven p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); 4327d82bf34SGeert Uytterhoeven if (!p) 4337d82bf34SGeert Uytterhoeven return -ENOMEM; 434119f5e44SMagnus Damm 435a53f7953SVladimir Zapolskiy p->dev = dev; 436119f5e44SMagnus Damm spin_lock_init(&p->lock); 437119f5e44SMagnus Damm 4388b092be9SGeert Uytterhoeven /* Get device configuration from DT node */ 4398b092be9SGeert Uytterhoeven ret = gpio_rcar_parse_dt(p, &npins); 440850dfe17SLaurent Pinchart if (ret < 0) 441850dfe17SLaurent Pinchart return ret; 442159f8a02SLaurent Pinchart 443159f8a02SLaurent Pinchart platform_set_drvdata(pdev, p); 444159f8a02SLaurent Pinchart 445df0c6c80SGeert Uytterhoeven pm_runtime_enable(dev); 446df0c6c80SGeert Uytterhoeven 447119f5e44SMagnus Damm irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 4485a24d4b6SSergei Shtylyov if (!irq) { 4495a24d4b6SSergei Shtylyov dev_err(dev, "missing IRQ\n"); 450119f5e44SMagnus Damm ret = -EINVAL; 451119f5e44SMagnus Damm goto err0; 452119f5e44SMagnus Damm } 453119f5e44SMagnus Damm 4545a24d4b6SSergei Shtylyov io = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4555a24d4b6SSergei Shtylyov p->base = devm_ioremap_resource(dev, io); 4565a24d4b6SSergei Shtylyov if (IS_ERR(p->base)) { 4575a24d4b6SSergei Shtylyov ret = PTR_ERR(p->base); 458119f5e44SMagnus Damm goto err0; 459119f5e44SMagnus Damm } 460119f5e44SMagnus Damm 461119f5e44SMagnus Damm gpio_chip = &p->gpio_chip; 462dc3465a9SLaurent Pinchart gpio_chip->request = gpio_rcar_request; 463dc3465a9SLaurent Pinchart gpio_chip->free = gpio_rcar_free; 464ad817297SGeert Uytterhoeven gpio_chip->get_direction = gpio_rcar_get_direction; 465119f5e44SMagnus Damm gpio_chip->direction_input = gpio_rcar_direction_input; 466119f5e44SMagnus Damm gpio_chip->get = gpio_rcar_get; 467119f5e44SMagnus Damm gpio_chip->direction_output = gpio_rcar_direction_output; 468119f5e44SMagnus Damm gpio_chip->set = gpio_rcar_set; 469dbb763b8SGeert Uytterhoeven gpio_chip->set_multiple = gpio_rcar_set_multiple; 470119f5e44SMagnus Damm gpio_chip->label = name; 47158383c78SLinus Walleij gpio_chip->parent = dev; 472119f5e44SMagnus Damm gpio_chip->owner = THIS_MODULE; 4738b092be9SGeert Uytterhoeven gpio_chip->base = -1; 4748b092be9SGeert Uytterhoeven gpio_chip->ngpio = npins; 475119f5e44SMagnus Damm 476119f5e44SMagnus Damm irq_chip = &p->irq_chip; 477119f5e44SMagnus Damm irq_chip->name = name; 47847bd38a3SNiklas Söderlund irq_chip->parent_device = dev; 479119f5e44SMagnus Damm irq_chip->irq_mask = gpio_rcar_irq_disable; 480119f5e44SMagnus Damm irq_chip->irq_unmask = gpio_rcar_irq_enable; 481119f5e44SMagnus Damm irq_chip->irq_set_type = gpio_rcar_irq_set_type; 482ab82fa7dSGeert Uytterhoeven irq_chip->irq_set_wake = gpio_rcar_irq_set_wake; 483ab82fa7dSGeert Uytterhoeven irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; 484119f5e44SMagnus Damm 485c7b6f457SLinus Walleij ret = gpiochip_add_data(gpio_chip, p); 486c7f3c5d3SGeert Uytterhoeven if (ret) { 487c7f3c5d3SGeert Uytterhoeven dev_err(dev, "failed to add GPIO controller\n"); 4880c8aab8eSDan Carpenter goto err0; 489119f5e44SMagnus Damm } 490119f5e44SMagnus Damm 4918b092be9SGeert Uytterhoeven ret = gpiochip_irqchip_add(gpio_chip, irq_chip, 0, handle_level_irq, 4928b092be9SGeert Uytterhoeven IRQ_TYPE_NONE); 493c7f3c5d3SGeert Uytterhoeven if (ret) { 494c7f3c5d3SGeert Uytterhoeven dev_err(dev, "cannot add irqchip\n"); 495c7f3c5d3SGeert Uytterhoeven goto err1; 496c7f3c5d3SGeert Uytterhoeven } 497c7f3c5d3SGeert Uytterhoeven 498ab82fa7dSGeert Uytterhoeven p->irq_parent = irq->start; 499b22978fcSGeert Uytterhoeven if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler, 500b22978fcSGeert Uytterhoeven IRQF_SHARED, name, p)) { 501b22978fcSGeert Uytterhoeven dev_err(dev, "failed to request IRQ\n"); 502119f5e44SMagnus Damm ret = -ENOENT; 503119f5e44SMagnus Damm goto err1; 504119f5e44SMagnus Damm } 505119f5e44SMagnus Damm 5068b092be9SGeert Uytterhoeven dev_info(dev, "driving %d GPIOs\n", npins); 507dc3465a9SLaurent Pinchart 508119f5e44SMagnus Damm return 0; 509119f5e44SMagnus Damm 510119f5e44SMagnus Damm err1: 5114d84b9e4SGeert Uytterhoeven gpiochip_remove(gpio_chip); 512119f5e44SMagnus Damm err0: 513df0c6c80SGeert Uytterhoeven pm_runtime_disable(dev); 514119f5e44SMagnus Damm return ret; 515119f5e44SMagnus Damm } 516119f5e44SMagnus Damm 517119f5e44SMagnus Damm static int gpio_rcar_remove(struct platform_device *pdev) 518119f5e44SMagnus Damm { 519119f5e44SMagnus Damm struct gpio_rcar_priv *p = platform_get_drvdata(pdev); 520119f5e44SMagnus Damm 5219f5132aeSabdoulaye berthe gpiochip_remove(&p->gpio_chip); 522119f5e44SMagnus Damm 523df0c6c80SGeert Uytterhoeven pm_runtime_disable(&pdev->dev); 524119f5e44SMagnus Damm return 0; 525119f5e44SMagnus Damm } 526119f5e44SMagnus Damm 52751750fb1SHien Dang #ifdef CONFIG_PM_SLEEP 52851750fb1SHien Dang static int gpio_rcar_suspend(struct device *dev) 52951750fb1SHien Dang { 53051750fb1SHien Dang struct gpio_rcar_priv *p = dev_get_drvdata(dev); 53151750fb1SHien Dang 53251750fb1SHien Dang p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL); 53351750fb1SHien Dang p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL); 53451750fb1SHien Dang p->bank_info.outdt = gpio_rcar_read(p, OUTDT); 53551750fb1SHien Dang p->bank_info.intmsk = gpio_rcar_read(p, INTMSK); 53651750fb1SHien Dang p->bank_info.posneg = gpio_rcar_read(p, POSNEG); 53751750fb1SHien Dang p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL); 53851750fb1SHien Dang if (p->has_both_edge_trigger) 53951750fb1SHien Dang p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE); 54051750fb1SHien Dang 5419ac79ba9SGeert Uytterhoeven if (atomic_read(&p->wakeup_path)) 5429ac79ba9SGeert Uytterhoeven device_set_wakeup_path(dev); 5439ac79ba9SGeert Uytterhoeven 54451750fb1SHien Dang return 0; 54551750fb1SHien Dang } 54651750fb1SHien Dang 54751750fb1SHien Dang static int gpio_rcar_resume(struct device *dev) 54851750fb1SHien Dang { 54951750fb1SHien Dang struct gpio_rcar_priv *p = dev_get_drvdata(dev); 55051750fb1SHien Dang unsigned int offset; 55151750fb1SHien Dang u32 mask; 55251750fb1SHien Dang 55351750fb1SHien Dang for (offset = 0; offset < p->gpio_chip.ngpio; offset++) { 554496069b8SBiju Das if (!gpiochip_line_is_valid(&p->gpio_chip, offset)) 555496069b8SBiju Das continue; 556496069b8SBiju Das 55751750fb1SHien Dang mask = BIT(offset); 55851750fb1SHien Dang /* I/O pin */ 55951750fb1SHien Dang if (!(p->bank_info.iointsel & mask)) { 56051750fb1SHien Dang if (p->bank_info.inoutsel & mask) 56151750fb1SHien Dang gpio_rcar_direction_output( 56251750fb1SHien Dang &p->gpio_chip, offset, 56351750fb1SHien Dang !!(p->bank_info.outdt & mask)); 56451750fb1SHien Dang else 56551750fb1SHien Dang gpio_rcar_direction_input(&p->gpio_chip, 56651750fb1SHien Dang offset); 56751750fb1SHien Dang } else { 56851750fb1SHien Dang /* Interrupt pin */ 56951750fb1SHien Dang gpio_rcar_config_interrupt_input_mode( 57051750fb1SHien Dang p, 57151750fb1SHien Dang offset, 57251750fb1SHien Dang !(p->bank_info.posneg & mask), 57351750fb1SHien Dang !(p->bank_info.edglevel & mask), 57451750fb1SHien Dang !!(p->bank_info.bothedge & mask)); 57551750fb1SHien Dang 57651750fb1SHien Dang if (p->bank_info.intmsk & mask) 57751750fb1SHien Dang gpio_rcar_write(p, MSKCLR, mask); 57851750fb1SHien Dang } 57951750fb1SHien Dang } 58051750fb1SHien Dang 58151750fb1SHien Dang return 0; 58251750fb1SHien Dang } 58351750fb1SHien Dang #endif /* CONFIG_PM_SLEEP*/ 58451750fb1SHien Dang 58551750fb1SHien Dang static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); 58651750fb1SHien Dang 587119f5e44SMagnus Damm static struct platform_driver gpio_rcar_device_driver = { 588119f5e44SMagnus Damm .probe = gpio_rcar_probe, 589119f5e44SMagnus Damm .remove = gpio_rcar_remove, 590119f5e44SMagnus Damm .driver = { 591119f5e44SMagnus Damm .name = "gpio_rcar", 59251750fb1SHien Dang .pm = &gpio_rcar_pm_ops, 593159f8a02SLaurent Pinchart .of_match_table = of_match_ptr(gpio_rcar_of_table), 594119f5e44SMagnus Damm } 595119f5e44SMagnus Damm }; 596119f5e44SMagnus Damm 597119f5e44SMagnus Damm module_platform_driver(gpio_rcar_device_driver); 598119f5e44SMagnus Damm 599119f5e44SMagnus Damm MODULE_AUTHOR("Magnus Damm"); 600119f5e44SMagnus Damm MODULE_DESCRIPTION("Renesas R-Car GPIO Driver"); 601119f5e44SMagnus Damm MODULE_LICENSE("GPL v2"); 602