From: Michal Wilczynski Date: Fri, 6 Dec 2024 16:51:00 +0000 (+0100) Subject: net: rfkill: rfkill-gpio: Add workaround for faulty interconnect design X-Git-Tag: accepted/tizen/unified/20241216.185838~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=03d2ee0797b59d9ad713286c62ce6e7b3a5c4be3;p=platform%2Fkernel%2Flinux-thead.git net: rfkill: rfkill-gpio: Add workaround for faulty interconnect design Vendor provided custom rfkill drivers seems to restrict operation of rfkill to only enable the Wi-Fi/Bluetooth at the start. The flipping on/off is restricted. Additionally they include a lot of delays when accessing GPIO controller pcal6408ahk_c. This seems to indicate some instability in the hardware. So I spent some more time trying to analyze this, and here's what I found: 1) The delays are not really needed. The GPIO controller and i2c bus seems to work correctly as designed. So I don't think any modifications in the GPIO/i2c drivers are appropriate. 2) The thing that is really needed is the state change on the GPIO line connected to the wireless chip i.e the value on the GPIO is already 1 on the boot, but it needs to go from 0->1 for Wi-Fi chip to get up properly. In the new implementation work around this by introducing new option 'edge-triggered'. This allows for the the state change on GPIO 0->1, which seems to be required by the wireless chip to start working correctly. There is still msleep() required between flipping GPIO values, it seems like there is no way around this. Nevertheless in the new implementation it should be possible to turn the power on/off to Wi-Fi and Bluetooth programatically, through this crashed the driver in my testing. Change-Id: I1181edf61d036e91aad8d7bb9bd79684296384dd Signed-off-by: Michal Wilczynski --- diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 4e32d659524e..d1980962ee3f 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -14,6 +14,7 @@ #include #include #include +#include struct rfkill_gpio_data { const char *name; @@ -25,8 +26,17 @@ struct rfkill_gpio_data { struct clk *clk; bool clk_enabled; + bool blocked; + bool edge_triggered; }; +static void rfkill_gpiod_toggle(struct gpio_desc *desc, bool blocked) +{ + gpiod_set_value_cansleep(desc, blocked); + msleep(10); + gpiod_set_value_cansleep(desc, !blocked); +} + static int rfkill_gpio_set_power(void *data, bool blocked) { struct rfkill_gpio_data *rfkill = data; @@ -34,8 +44,17 @@ static int rfkill_gpio_set_power(void *data, bool blocked) if (!blocked && !IS_ERR(rfkill->clk) && !rfkill->clk_enabled) clk_enable(rfkill->clk); - gpiod_set_value_cansleep(rfkill->shutdown_gpio, !blocked); - gpiod_set_value_cansleep(rfkill->reset_gpio, !blocked); + if (rfkill->edge_triggered && + rfkill->blocked != blocked) { + rfkill_gpiod_toggle(rfkill->shutdown_gpio, blocked); + rfkill_gpiod_toggle(rfkill->reset_gpio, blocked); + + rfkill->blocked = blocked; + } else { + gpiod_set_value_cansleep(rfkill->shutdown_gpio, !blocked); + gpiod_set_value_cansleep(rfkill->reset_gpio, !blocked); + } + if (blocked && !IS_ERR(rfkill->clk) && rfkill->clk_enabled) clk_disable(rfkill->clk); @@ -106,6 +125,9 @@ static int rfkill_gpio_probe(struct platform_device *pdev) return ret; } + rfkill->edge_triggered = + device_property_read_bool(&pdev->dev, "edge-triggered"); + rfkill->clk = devm_clk_get(&pdev->dev, NULL); gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_ASIS); @@ -140,6 +162,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (!rfkill->rfkill_dev) return -ENOMEM; + rfkill->blocked = true; + ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) goto err_destroy;