Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | // SPDX-License-Identifier: GPL-2.0-only /* * gpio-reg: single register individually fixed-direction GPIOs * * Copyright (C) 2016 Russell King */ #include <linux/gpio/driver.h> #include <linux/gpio/gpio-reg.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/spinlock.h> struct gpio_reg { struct gpio_chip gc; spinlock_t lock; u32 direction; u32 out; void __iomem *reg; struct irq_domain *irqdomain; const int *irqs; }; #define to_gpio_reg(x) container_of(x, struct gpio_reg, gc) static int gpio_reg_get_direction(struct gpio_chip *gc, unsigned offset) { struct gpio_reg *r = to_gpio_reg(gc); return r->direction & BIT(offset) ? 1 : 0; } static int gpio_reg_direction_output(struct gpio_chip *gc, unsigned offset, int value) { struct gpio_reg *r = to_gpio_reg(gc); if (r->direction & BIT(offset)) return -ENOTSUPP; gc->set(gc, offset, value); return 0; } static int gpio_reg_direction_input(struct gpio_chip *gc, unsigned offset) { struct gpio_reg *r = to_gpio_reg(gc); return r->direction & BIT(offset) ? 0 : -ENOTSUPP; } static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; u32 val, mask = BIT(offset); spin_lock_irqsave(&r->lock, flags); val = r->out; if (value) val |= mask; else val &= ~mask; r->out = val; writel_relaxed(val, r->reg); spin_unlock_irqrestore(&r->lock, flags); } static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) { struct gpio_reg *r = to_gpio_reg(gc); u32 val, mask = BIT(offset); if (r->direction & mask) { /* * double-read the value, some registers latch after the * first read. */ readl_relaxed(r->reg); val = readl_relaxed(r->reg); } else { val = r->out; } return !!(val & mask); } static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; spin_lock_irqsave(&r->lock, flags); r->out = (r->out & ~*mask) | (*bits & *mask); writel_relaxed(r->out, r->reg); spin_unlock_irqrestore(&r->lock, flags); } static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset) { struct gpio_reg *r = to_gpio_reg(gc); int irq = r->irqs[offset]; if (irq >= 0 && r->irqdomain) irq = irq_find_mapping(r->irqdomain, irq); return irq; } /** * gpio_reg_init - add a fixed in/out register as gpio * @dev: optional struct device associated with this register * @base: start gpio number, or -1 to allocate * @num: number of GPIOs, maximum 32 * @label: GPIO chip label * @direction: bitmask of fixed direction, one per GPIO signal, 1 = in * @def_out: initial GPIO output value * @names: array of %num strings describing each GPIO signal or %NULL * @irqdom: irq domain or %NULL * @irqs: array of %num ints describing the interrupt mapping for each * GPIO signal, or %NULL. If @irqdom is %NULL, then this * describes the Linux interrupt number, otherwise it describes * the hardware interrupt number in the specified irq domain. * * Add a single-register GPIO device containing up to 32 GPIO signals, * where each GPIO has a fixed input or output configuration. Only * input GPIOs are assumed to be readable from the register, and only * then after a double-read. Output values are assumed not to be * readable. */ struct gpio_chip *gpio_reg_init(struct device *dev, void __iomem *reg, int base, int num, const char *label, u32 direction, u32 def_out, const char *const *names, struct irq_domain *irqdom, const int *irqs) { struct gpio_reg *r; int ret; if (dev) r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL); else r = kzalloc(sizeof(*r), GFP_KERNEL); if (!r) return ERR_PTR(-ENOMEM); spin_lock_init(&r->lock); r->gc.label = label; r->gc.get_direction = gpio_reg_get_direction; r->gc.direction_input = gpio_reg_direction_input; r->gc.direction_output = gpio_reg_direction_output; r->gc.set = gpio_reg_set; r->gc.get = gpio_reg_get; r->gc.set_multiple = gpio_reg_set_multiple; if (irqs) r->gc.to_irq = gpio_reg_to_irq; r->gc.base = base; r->gc.ngpio = num; r->gc.names = names; r->direction = direction; r->out = def_out; r->reg = reg; r->irqs = irqs; if (dev) ret = devm_gpiochip_add_data(dev, &r->gc, r); else ret = gpiochip_add_data(&r->gc, r); return ret ? ERR_PTR(ret) : &r->gc; } int gpio_reg_resume(struct gpio_chip *gc) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; spin_lock_irqsave(&r->lock, flags); writel_relaxed(r->out, r->reg); spin_unlock_irqrestore(&r->lock, flags); return 0; } |