Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: rcar: select General Output Register to set output states

R-Car GPIO controller provides two interfaces to set GPIO line output
signal state, and for a particular GPIO line the selected interface is
determined by OUTDTSEL bit value.

At the moment the driver supports only one of two interfaces, namely
OUTDT General Output Register is used to control the output signal.

While this selection is the default one on reset, it is not explicitly
configured on probe, thus it might be possible that kernel and userspace
consumers of a GPIO won't be able to set the wanted GPIO output signal.

Below is a simple test case to reproduce the described problem and
verify this fix in the kernel on H3 ULCB by setting non-default OUTDTSEL
configuration from a bootloader:

u-boot > mw.l 0xe6055440 0x3000 1
...
userspace > echo default-on > /sys/devices/platform/leds/leds/led5/trigger
userspace > echo default-on > /sys/devices/platform/leds/leds/led6/trigger

Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Vladimir Zapolskiy and committed by
Linus Walleij
3ae4f3aa b597c3a9

+11 -1
+11 -1
drivers/gpio/gpio-rcar.c
··· 40 40 struct irq_chip irq_chip; 41 41 unsigned int irq_parent; 42 42 atomic_t wakeup_path; 43 + bool has_outdtsel; 43 44 bool has_both_edge_trigger; 44 45 struct gpio_rcar_bank_info bank_info; 45 46 }; ··· 56 55 #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ 57 56 #define EDGLEVEL 0x24 /* Edge/level Select Register */ 58 57 #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ 58 + #define OUTDTSEL 0x40 /* Output Data Select Register */ 59 59 #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ 60 60 61 61 #define RCAR_MAX_GPIO_PER_BANK 32 ··· 237 235 /* Select Input Mode or Output Mode in INOUTSEL */ 238 236 gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); 239 237 238 + /* Select General Output Register to output data in OUTDTSEL */ 239 + if (p->has_outdtsel && output) 240 + gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); 241 + 240 242 spin_unlock_irqrestore(&p->lock, flags); 241 243 } 242 244 ··· 342 336 } 343 337 344 338 struct gpio_rcar_info { 339 + bool has_outdtsel; 345 340 bool has_both_edge_trigger; 346 341 }; 347 342 348 343 static const struct gpio_rcar_info gpio_rcar_info_gen1 = { 344 + .has_outdtsel = false, 349 345 .has_both_edge_trigger = false, 350 346 }; 351 347 352 348 static const struct gpio_rcar_info gpio_rcar_info_gen2 = { 349 + .has_outdtsel = true, 353 350 .has_both_edge_trigger = true, 354 351 }; 355 352 ··· 412 403 int ret; 413 404 414 405 info = of_device_get_match_data(p->dev); 406 + p->has_outdtsel = info->has_outdtsel; 407 + p->has_both_edge_trigger = info->has_both_edge_trigger; 415 408 416 409 ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); 417 410 *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; 418 - p->has_both_edge_trigger = info->has_both_edge_trigger; 419 411 420 412 if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { 421 413 dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",