Skip to content

Commit ee0175b

Browse files
svanheulebrgl
authored andcommitted
gpio: realtek-otto: switch to 32-bit I/O
By using 16-bit I/O on the GPIO peripheral, which is apparently not safe on MIPS, the IMR can end up containing garbage. This then results in interrupt triggers for lines that don't have an interrupt handler associated. The irq_desc lookup fails, and the ISR will not be cleared, keeping the CPU busy until reboot, or until another IMR operation restores the correct value. This situation appears to happen very rarely, for < 0.5% of IMR writes. Instead of using 8-bit or 16-bit I/O operations on the 32-bit memory mapped peripheral registers, switch to using 32-bit I/O only, operating on the entire bank for all single bit line settings. For 2-bit line settings, with 16-bit port values, stick to manual (un)packing. This issue has been seen on RTL8382M (HPE 1920-16G), RTL8391M (Netgear GS728TP v2), and RTL8393M (D-Link DGS-1210-52 F3, Zyxel GS1900-48). Reported-by: Luiz Angelo Daros de Luca <luizluca@gmail.com> # DGS-1210-52 Reported-by: Birger Koblitz <mail@birger-koblitz.de> # GS728TP Reported-by: Jan Hoffmann <jan@3e8.eu> # 1920-16G Fixes: 0d82fb1 ("gpio: Add Realtek Otto GPIO support") Signed-off-by: Sander Vanheule <sander@svanheule.net> Cc: Paul Cercueil <paul@crapouillou.net> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
1 parent 518e26f commit ee0175b

File tree

1 file changed

+85
-81
lines changed

1 file changed

+85
-81
lines changed

drivers/gpio/gpio-realtek-otto.c

Lines changed: 85 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,20 @@
4646
* @lock: Lock for accessing the IRQ registers and values
4747
* @intr_mask: Mask for interrupts lines
4848
* @intr_type: Interrupt type selection
49+
* @bank_read: Read a bank setting as a single 32-bit value
50+
* @bank_write: Write a bank setting as a single 32-bit value
51+
* @imr_line_pos: Bit shift of an IRQ line's IMR value.
52+
*
53+
* The DIR, DATA, and ISR registers consist of four 8-bit port values, packed
54+
* into a single 32-bit register. Use @bank_read (@bank_write) to get (assign)
55+
* a value from (to) these registers. The IMR register consists of four 16-bit
56+
* port values, packed into two 32-bit registers. Use @imr_line_pos to get the
57+
* bit shift of the 2-bit field for a line's IMR settings. Shifts larger than
58+
* 32 overflow into the second register.
4959
*
5060
* Because the interrupt mask register (IMR) combines the function of IRQ type
5161
* selection and masking, two extra values are stored. @intr_mask is used to
52-
* mask/unmask the interrupts for a GPIO port, and @intr_type is used to store
62+
* mask/unmask the interrupts for a GPIO line, and @intr_type is used to store
5363
* the selected interrupt types. The logical AND of these values is written to
5464
* IMR on changes.
5565
*/
@@ -59,10 +69,11 @@ struct realtek_gpio_ctrl {
5969
void __iomem *cpumask_base;
6070
struct cpumask cpu_irq_maskable;
6171
raw_spinlock_t lock;
62-
u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
63-
u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
64-
unsigned int (*port_offset_u8)(unsigned int port);
65-
unsigned int (*port_offset_u16)(unsigned int port);
72+
u8 intr_mask[REALTEK_GPIO_MAX];
73+
u8 intr_type[REALTEK_GPIO_MAX];
74+
u32 (*bank_read)(void __iomem *reg);
75+
void (*bank_write)(void __iomem *reg, u32 value);
76+
unsigned int (*line_imr_pos)(unsigned int line);
6677
};
6778

6879
/* Expand with more flags as devices with other quirks are added */
@@ -101,14 +112,22 @@ static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
101112
* port. The two interrupt mask registers store two bits per GPIO, so use u16
102113
* values.
103114
*/
104-
static unsigned int realtek_gpio_port_offset_u8(unsigned int port)
115+
static u32 realtek_gpio_bank_read_swapped(void __iomem *reg)
105116
{
106-
return port;
117+
return ioread32be(reg);
107118
}
108119

109-
static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
120+
static void realtek_gpio_bank_write_swapped(void __iomem *reg, u32 value)
110121
{
111-
return 2 * port;
122+
iowrite32be(value, reg);
123+
}
124+
125+
static unsigned int realtek_gpio_line_imr_pos_swapped(unsigned int line)
126+
{
127+
unsigned int port_pin = line % 8;
128+
unsigned int port = line / 8;
129+
130+
return 2 * (8 * (port ^ 1) + port_pin);
112131
}
113132

114133
/*
@@ -119,83 +138,79 @@ static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
119138
* per GPIO, so use u16 values. The first register contains ports 1 and 0, the
120139
* second ports 3 and 2.
121140
*/
122-
static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port)
141+
static u32 realtek_gpio_bank_read(void __iomem *reg)
123142
{
124-
return 3 - port;
143+
return ioread32(reg);
125144
}
126145

127-
static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
146+
static void realtek_gpio_bank_write(void __iomem *reg, u32 value)
128147
{
129-
return 2 * (port ^ 1);
148+
iowrite32(value, reg);
130149
}
131150

132-
static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
133-
unsigned int port, u16 irq_type, u16 irq_mask)
151+
static unsigned int realtek_gpio_line_imr_pos(unsigned int line)
134152
{
135-
iowrite16(irq_type & irq_mask,
136-
ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
153+
return 2 * line;
137154
}
138155

139-
static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
140-
unsigned int port, u8 mask)
156+
static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, u32 mask)
141157
{
142-
iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
158+
ctrl->bank_write(ctrl->base + REALTEK_GPIO_REG_ISR, mask);
143159
}
144160

145-
static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
161+
static u32 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl)
146162
{
147-
return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
163+
return ctrl->bank_read(ctrl->base + REALTEK_GPIO_REG_ISR);
148164
}
149165

150-
/* Set the rising and falling edge mask bits for a GPIO port pin */
151-
static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value)
166+
/* Set the rising and falling edge mask bits for a GPIO pin */
167+
static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigned int line)
152168
{
153-
return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin;
169+
void __iomem *reg = ctrl->base + REALTEK_GPIO_REG_IMR;
170+
unsigned int line_shift = ctrl->line_imr_pos(line);
171+
unsigned int shift = line_shift % 32;
172+
u32 irq_type = ctrl->intr_type[line];
173+
u32 irq_mask = ctrl->intr_mask[line];
174+
u32 reg_val;
175+
176+
reg += 4 * (line_shift / 32);
177+
reg_val = ioread32(reg);
178+
reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift);
179+
reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift;
180+
iowrite32(reg_val, reg);
154181
}
155182

156183
static void realtek_gpio_irq_ack(struct irq_data *data)
157184
{
158185
struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
159186
irq_hw_number_t line = irqd_to_hwirq(data);
160-
unsigned int port = line / 8;
161-
unsigned int port_pin = line % 8;
162187

163-
realtek_gpio_clear_isr(ctrl, port, BIT(port_pin));
188+
realtek_gpio_clear_isr(ctrl, BIT(line));
164189
}
165190

166191
static void realtek_gpio_irq_unmask(struct irq_data *data)
167192
{
168193
struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
169194
unsigned int line = irqd_to_hwirq(data);
170-
unsigned int port = line / 8;
171-
unsigned int port_pin = line % 8;
172195
unsigned long flags;
173-
u16 m;
174196

175197
gpiochip_enable_irq(&ctrl->gc, line);
176198

177199
raw_spin_lock_irqsave(&ctrl->lock, flags);
178-
m = ctrl->intr_mask[port];
179-
m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
180-
ctrl->intr_mask[port] = m;
181-
realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
200+
ctrl->intr_mask[line] = REALTEK_GPIO_IMR_LINE_MASK;
201+
realtek_gpio_update_line_imr(ctrl, line);
182202
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
183203
}
184204

185205
static void realtek_gpio_irq_mask(struct irq_data *data)
186206
{
187207
struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
188208
unsigned int line = irqd_to_hwirq(data);
189-
unsigned int port = line / 8;
190-
unsigned int port_pin = line % 8;
191209
unsigned long flags;
192-
u16 m;
193210

194211
raw_spin_lock_irqsave(&ctrl->lock, flags);
195-
m = ctrl->intr_mask[port];
196-
m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
197-
ctrl->intr_mask[port] = m;
198-
realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
212+
ctrl->intr_mask[line] = 0;
213+
realtek_gpio_update_line_imr(ctrl, line);
199214
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
200215

201216
gpiochip_disable_irq(&ctrl->gc, line);
@@ -205,10 +220,8 @@ static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_ty
205220
{
206221
struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
207222
unsigned int line = irqd_to_hwirq(data);
208-
unsigned int port = line / 8;
209-
unsigned int port_pin = line % 8;
210223
unsigned long flags;
211-
u16 type, t;
224+
u8 type;
212225

213226
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
214227
case IRQ_TYPE_EDGE_FALLING:
@@ -227,11 +240,8 @@ static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_ty
227240
irq_set_handler_locked(data, handle_edge_irq);
228241

229242
raw_spin_lock_irqsave(&ctrl->lock, flags);
230-
t = ctrl->intr_type[port];
231-
t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
232-
t |= realtek_gpio_imr_bits(port_pin, type);
233-
ctrl->intr_type[port] = t;
234-
realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]);
243+
ctrl->intr_type[line] = type;
244+
realtek_gpio_update_line_imr(ctrl, line);
235245
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
236246

237247
return 0;
@@ -242,57 +252,48 @@ static void realtek_gpio_irq_handler(struct irq_desc *desc)
242252
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
243253
struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
244254
struct irq_chip *irq_chip = irq_desc_get_chip(desc);
245-
unsigned int lines_done;
246-
unsigned int port_pin_count;
247255
unsigned long status;
248256
int offset;
249257

250258
chained_irq_enter(irq_chip, desc);
251259

252-
for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) {
253-
status = realtek_gpio_read_isr(ctrl, lines_done / 8);
254-
port_pin_count = min(gc->ngpio - lines_done, 8U);
255-
for_each_set_bit(offset, &status, port_pin_count)
256-
generic_handle_domain_irq(gc->irq.domain, offset + lines_done);
257-
}
260+
status = realtek_gpio_read_isr(ctrl);
261+
for_each_set_bit(offset, &status, gc->ngpio)
262+
generic_handle_domain_irq(gc->irq.domain, offset);
258263

259264
chained_irq_exit(irq_chip, desc);
260265
}
261266

262-
static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl,
263-
unsigned int port, int cpu)
267+
static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, int cpu)
264268
{
265-
return ctrl->cpumask_base + ctrl->port_offset_u8(port) +
266-
REALTEK_GPIO_PORTS_PER_BANK * cpu;
269+
return ctrl->cpumask_base + REALTEK_GPIO_PORTS_PER_BANK * cpu;
267270
}
268271

269272
static int realtek_gpio_irq_set_affinity(struct irq_data *data,
270273
const struct cpumask *dest, bool force)
271274
{
272275
struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
273276
unsigned int line = irqd_to_hwirq(data);
274-
unsigned int port = line / 8;
275-
unsigned int port_pin = line % 8;
276277
void __iomem *irq_cpu_mask;
277278
unsigned long flags;
278279
int cpu;
279-
u8 v;
280+
u32 v;
280281

281282
if (!ctrl->cpumask_base)
282283
return -ENXIO;
283284

284285
raw_spin_lock_irqsave(&ctrl->lock, flags);
285286

286287
for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
287-
irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
288-
v = ioread8(irq_cpu_mask);
288+
irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu);
289+
v = ctrl->bank_read(irq_cpu_mask);
289290

290291
if (cpumask_test_cpu(cpu, dest))
291-
v |= BIT(port_pin);
292+
v |= BIT(line);
292293
else
293-
v &= ~BIT(port_pin);
294+
v &= ~BIT(line);
294295

295-
iowrite8(v, irq_cpu_mask);
296+
ctrl->bank_write(irq_cpu_mask, v);
296297
}
297298

298299
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
@@ -305,16 +306,17 @@ static int realtek_gpio_irq_set_affinity(struct irq_data *data,
305306
static int realtek_gpio_irq_init(struct gpio_chip *gc)
306307
{
307308
struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
308-
unsigned int port;
309+
u32 mask_all = GENMASK(gc->ngpio - 1, 0);
310+
unsigned int line;
309311
int cpu;
310312

311-
for (port = 0; (port * 8) < gc->ngpio; port++) {
312-
realtek_gpio_write_imr(ctrl, port, 0, 0);
313-
realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
313+
for (line = 0; line < gc->ngpio; line++)
314+
realtek_gpio_update_line_imr(ctrl, line);
314315

315-
for_each_cpu(cpu, &ctrl->cpu_irq_maskable)
316-
iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu));
317-
}
316+
realtek_gpio_clear_isr(ctrl, mask_all);
317+
318+
for_each_cpu(cpu, &ctrl->cpu_irq_maskable)
319+
ctrl->bank_write(realtek_gpio_irq_cpu_mask(ctrl, cpu), mask_all);
318320

319321
return 0;
320322
}
@@ -387,12 +389,14 @@ static int realtek_gpio_probe(struct platform_device *pdev)
387389

388390
if (dev_flags & GPIO_PORTS_REVERSED) {
389391
bgpio_flags = 0;
390-
ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev;
391-
ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev;
392+
ctrl->bank_read = realtek_gpio_bank_read;
393+
ctrl->bank_write = realtek_gpio_bank_write;
394+
ctrl->line_imr_pos = realtek_gpio_line_imr_pos;
392395
} else {
393396
bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
394-
ctrl->port_offset_u8 = realtek_gpio_port_offset_u8;
395-
ctrl->port_offset_u16 = realtek_gpio_port_offset_u16;
397+
ctrl->bank_read = realtek_gpio_bank_read_swapped;
398+
ctrl->bank_write = realtek_gpio_bank_write_swapped;
399+
ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped;
396400
}
397401

398402
err = bgpio_init(&ctrl->gc, dev, 4,

0 commit comments

Comments
 (0)