From fb13053192fcc96c18cf659869d9c9b2ca58c559 Mon Sep 17 00:00:00 2001 From: Wolfvak Date: Thu, 10 Jun 2021 22:38:46 -0300 Subject: [PATCH 1/3] driver bugfix vanilla sc16is7xx driver has a bug where it registers the gpiochip _before_ setting up data needed by the gpio functions themselves usually this doesn't cause issues because everything will be set up properly by the time the driver finishes loading, but if there's a child gpio hog node present it'll try to add it and immediately access uninitialized memory, leading to a kernel panic this should also be fixed in mainline --- drivers/tty/serial/sc16is7xx.c | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f86ec2d2635b7c..1a1e23bcefeb13 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1240,25 +1240,6 @@ static int sc16is7xx_probe(struct device *dev, } sched_set_fifo(s->kworker_task); -#ifdef CONFIG_GPIOLIB - if (devtype->nr_gpio) { - /* Setup GPIO cotroller */ - s->gpio.owner = THIS_MODULE; - s->gpio.parent = dev; - s->gpio.label = dev_name(dev); - s->gpio.direction_input = sc16is7xx_gpio_direction_input; - s->gpio.get = sc16is7xx_gpio_get; - s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; - s->gpio.base = -1; - s->gpio.ngpio = devtype->nr_gpio; - s->gpio.can_sleep = 1; - ret = gpiochip_add_data(&s->gpio, s); - if (ret) - goto out_thread; - } -#endif - /* reset device, purging any pending irq / data */ regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, SC16IS7XX_IOCONTROL_SRESET_BIT); @@ -1313,6 +1294,25 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_power(&s->p[i].port, 0); } +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.parent = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add_data(&s->gpio, s); + if (ret) + goto out_thread; + } +#endif + if (dev->of_node) { struct property *prop; const __be32 *p; From 157fea808263fa1fa9d78b144115ae389ed89f1b Mon Sep 17 00:00:00 2001 From: Wolfvak Date: Fri, 11 Jun 2021 18:50:07 -0300 Subject: [PATCH 2/3] also reorder the exit order in case of error for the sc16is7xx driver --- drivers/tty/serial/sc16is7xx.c | 44 ++++++++++++++++------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 1a1e23bcefeb13..c2df48df184979 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1161,15 +1161,11 @@ static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, { struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; - u8 state = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG); - if (val) - state |= BIT(offset); - else - state &= ~BIT(offset); - sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state); sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), BIT(offset)); + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); return 0; } @@ -1260,7 +1256,7 @@ static int sc16is7xx_probe(struct device *dev, s->p[i].port.line = sc16is7xx_alloc_line(); if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { ret = -ENOMEM; - goto out_ports; + goto out_thread; } /* Disable all interrupts */ @@ -1294,6 +1290,17 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_power(&s->p[i].port, 0); } + if (dev->of_node) { + struct property *prop; + const __be32 *p; + u32 u; + + of_property_for_each_u32(dev->of_node, "irda-mode-ports", + prop, p, u) + if (u < devtype->nr_uart) + s->p[u].irda_mode = true; + } + #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) { /* Setup GPIO cotroller */ @@ -1309,21 +1316,10 @@ static int sc16is7xx_probe(struct device *dev, s->gpio.can_sleep = 1; ret = gpiochip_add_data(&s->gpio, s); if (ret) - goto out_thread; + goto out_ports; } #endif - if (dev->of_node) { - struct property *prop; - const __be32 *p; - u32 u; - - of_property_for_each_u32(dev->of_node, "irda-mode-ports", - prop, p, u) - if (u < devtype->nr_uart) - s->p[u].irda_mode = true; - } - /* * Setup interrupt. We first try to acquire the IRQ line as level IRQ. * If that succeeds, we can allow sharing the interrupt as well. @@ -1343,18 +1339,18 @@ static int sc16is7xx_probe(struct device *dev, if (!ret) return 0; +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) + gpiochip_remove(&s->gpio); + out_ports: +#endif for (i--; i >= 0; i--) { uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); clear_bit(s->p[i].port.line, &sc16is7xx_lines); } -#ifdef CONFIG_GPIOLIB - if (devtype->nr_gpio) - gpiochip_remove(&s->gpio); - out_thread: -#endif kthread_stop(s->kworker_task); out_clk: From 91c34247fd1a67dd72607c570aa7fdc5609ae247 Mon Sep 17 00:00:00 2001 From: Wolfvak Date: Fri, 11 Jun 2021 18:53:33 -0300 Subject: [PATCH 3/3] ir transceiver is now working on pure uart mode eventually a better driver will need to be written to handle the PWDOWN pin during low power states thanks to @al3x10m for all the testing and help --- arch/arm/boot/dts/nintendo3ds.dtsi | 28 ++++++++++++++++++++++++++- arch/arm/boot/dts/nintendo3ds_ktr.dts | 10 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/nintendo3ds.dtsi b/arch/arm/boot/dts/nintendo3ds.dtsi index 1b30e3045b80b5..05c473cfb55dbf 100755 --- a/arch/arm/boot/dts/nintendo3ds.dtsi +++ b/arch/arm/boot/dts/nintendo3ds.dtsi @@ -125,6 +125,20 @@ interrupt-controller; #interrupt-cells = <2>; + + txrc_ir_hog { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "IR_TXRC"; + }; + + rx_ir_hog { + gpio-hog; + gpios = <5 0>; + input; + line-name = "IR_RXD"; + }; }; gpio4: gpio-controller@10147028 { @@ -241,7 +255,7 @@ interrupts = ; - sc16is750: infrared@4d { + sc16is7xx: infrared@4d { compatible = "nxp,sc16is750"; reg = <0x4d>; clocks = <&irclk>; @@ -250,6 +264,18 @@ gpio-controller; #gpio-cells = <2>; + + /* + * The SC16IS7X0 is connected to a ROHM RPM841 IrDA module + * This module has a PWDOWN input that needs to be + * kept low if we want data to come through + */ + pwdown_low_hog { + gpio-hog; + gpios = <0 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "PWDOWN"; + }; }; }; diff --git a/arch/arm/boot/dts/nintendo3ds_ktr.dts b/arch/arm/boot/dts/nintendo3ds_ktr.dts index 0ff4cabde180ae..a25af67ea15b03 100644 --- a/arch/arm/boot/dts/nintendo3ds_ktr.dts +++ b/arch/arm/boot/dts/nintendo3ds_ktr.dts @@ -52,3 +52,13 @@ bootargs = "keep_bootcon fbcon=rotate:1"; }; }; + +&sc16is7xx { + /* + * The new3DS has a SC16IS760 UART + * Override the default choice of SC16IS750 + * It doesn't make much of a difference since you + * can't really use the fast mode via I2C but w/e + */ + compatible = "nxp,sc16is760"; +};