1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 284130aacSRichard Genoud /* 384130aacSRichard Genoud * Helpers for controlling modem lines via GPIO 484130aacSRichard Genoud * 584130aacSRichard Genoud * Copyright (C) 2014 Paratronic S.A. 684130aacSRichard Genoud */ 784130aacSRichard Genoud 884130aacSRichard Genoud #include <linux/err.h> 984130aacSRichard Genoud #include <linux/device.h> 10ce59e48fSUwe Kleine-König #include <linux/irq.h> 1184130aacSRichard Genoud #include <linux/gpio/consumer.h> 1293b88774SAlexander Shiyan #include <linux/termios.h> 13ce59e48fSUwe Kleine-König #include <linux/serial_core.h> 1482a3f87fSRomain Izard #include <linux/module.h> 15d9948267SStefan Roese #include <linux/property.h> 1684130aacSRichard Genoud 1784130aacSRichard Genoud #include "serial_mctrl_gpio.h" 1884130aacSRichard Genoud 1984130aacSRichard Genoud struct mctrl_gpios { 20ce59e48fSUwe Kleine-König struct uart_port *port; 2184130aacSRichard Genoud struct gpio_desc *gpio[UART_GPIO_MAX]; 22ce59e48fSUwe Kleine-König int irq[UART_GPIO_MAX]; 23ce59e48fSUwe Kleine-König unsigned int mctrl_prev; 24ce59e48fSUwe Kleine-König bool mctrl_on; 2584130aacSRichard Genoud }; 2684130aacSRichard Genoud 2784130aacSRichard Genoud static const struct { 2884130aacSRichard Genoud const char *name; 2984130aacSRichard Genoud unsigned int mctrl; 304ad8e34dSAndy Shevchenko enum gpiod_flags flags; 3184130aacSRichard Genoud } mctrl_gpios_desc[UART_GPIO_MAX] = { 324ad8e34dSAndy Shevchenko { "cts", TIOCM_CTS, GPIOD_IN, }, 334ad8e34dSAndy Shevchenko { "dsr", TIOCM_DSR, GPIOD_IN, }, 344ad8e34dSAndy Shevchenko { "dcd", TIOCM_CD, GPIOD_IN, }, 354ad8e34dSAndy Shevchenko { "rng", TIOCM_RNG, GPIOD_IN, }, 364ad8e34dSAndy Shevchenko { "rts", TIOCM_RTS, GPIOD_OUT_LOW, }, 374ad8e34dSAndy Shevchenko { "dtr", TIOCM_DTR, GPIOD_OUT_LOW, }, 3884130aacSRichard Genoud }; 3984130aacSRichard Genoud 404ad8e34dSAndy Shevchenko static bool mctrl_gpio_flags_is_dir_out(unsigned int idx) 414ad8e34dSAndy Shevchenko { 424ad8e34dSAndy Shevchenko return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT; 434ad8e34dSAndy Shevchenko } 444ad8e34dSAndy Shevchenko 4584130aacSRichard Genoud void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) 4684130aacSRichard Genoud { 4784130aacSRichard Genoud enum mctrl_gpio_idx i; 48834296a3SRojhalat Ibrahim struct gpio_desc *desc_array[UART_GPIO_MAX]; 49b9762bebSJanusz Krzysztofik DECLARE_BITMAP(values, UART_GPIO_MAX); 50834296a3SRojhalat Ibrahim unsigned int count = 0; 5184130aacSRichard Genoud 52434be0aeSYegor Yefremov if (gpios == NULL) 53434be0aeSYegor Yefremov return; 54434be0aeSYegor Yefremov 5584130aacSRichard Genoud for (i = 0; i < UART_GPIO_MAX; i++) 564ad8e34dSAndy Shevchenko if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) { 57834296a3SRojhalat Ibrahim desc_array[count] = gpios->gpio[i]; 58b9762bebSJanusz Krzysztofik __assign_bit(count, values, 59b9762bebSJanusz Krzysztofik mctrl & mctrl_gpios_desc[i].mctrl); 60834296a3SRojhalat Ibrahim count++; 61834296a3SRojhalat Ibrahim } 6277588c14SJanusz Krzysztofik gpiod_set_array_value(count, desc_array, NULL, values); 6384130aacSRichard Genoud } 6484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_set); 6584130aacSRichard Genoud 6684130aacSRichard Genoud struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, 6784130aacSRichard Genoud enum mctrl_gpio_idx gidx) 6884130aacSRichard Genoud { 6937e3ab00SAdam Ford if (gpios == NULL) 7037e3ab00SAdam Ford return NULL; 7137e3ab00SAdam Ford 7284130aacSRichard Genoud return gpios->gpio[gidx]; 7384130aacSRichard Genoud } 7484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); 7584130aacSRichard Genoud 7684130aacSRichard Genoud unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) 7784130aacSRichard Genoud { 7884130aacSRichard Genoud enum mctrl_gpio_idx i; 7984130aacSRichard Genoud 80434be0aeSYegor Yefremov if (gpios == NULL) 81434be0aeSYegor Yefremov return *mctrl; 82434be0aeSYegor Yefremov 8384130aacSRichard Genoud for (i = 0; i < UART_GPIO_MAX; i++) { 844ad8e34dSAndy Shevchenko if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) { 8584130aacSRichard Genoud if (gpiod_get_value(gpios->gpio[i])) 8684130aacSRichard Genoud *mctrl |= mctrl_gpios_desc[i].mctrl; 8784130aacSRichard Genoud else 8884130aacSRichard Genoud *mctrl &= ~mctrl_gpios_desc[i].mctrl; 8984130aacSRichard Genoud } 9084130aacSRichard Genoud } 9184130aacSRichard Genoud 9284130aacSRichard Genoud return *mctrl; 9384130aacSRichard Genoud } 9484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_get); 9584130aacSRichard Genoud 96bf5cee68SYegor Yefremov unsigned int 97bf5cee68SYegor Yefremov mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl) 98bf5cee68SYegor Yefremov { 99bf5cee68SYegor Yefremov enum mctrl_gpio_idx i; 100bf5cee68SYegor Yefremov 101434be0aeSYegor Yefremov if (gpios == NULL) 102434be0aeSYegor Yefremov return *mctrl; 103434be0aeSYegor Yefremov 104bf5cee68SYegor Yefremov for (i = 0; i < UART_GPIO_MAX; i++) { 1054ad8e34dSAndy Shevchenko if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) { 106bf5cee68SYegor Yefremov if (gpiod_get_value(gpios->gpio[i])) 107bf5cee68SYegor Yefremov *mctrl |= mctrl_gpios_desc[i].mctrl; 108bf5cee68SYegor Yefremov else 109bf5cee68SYegor Yefremov *mctrl &= ~mctrl_gpios_desc[i].mctrl; 110bf5cee68SYegor Yefremov } 111bf5cee68SYegor Yefremov } 112bf5cee68SYegor Yefremov 113bf5cee68SYegor Yefremov return *mctrl; 114bf5cee68SYegor Yefremov } 115bf5cee68SYegor Yefremov EXPORT_SYMBOL_GPL(mctrl_gpio_get_outputs); 116bf5cee68SYegor Yefremov 1177d8c70d8SUwe Kleine-König struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) 11884130aacSRichard Genoud { 11984130aacSRichard Genoud struct mctrl_gpios *gpios; 12084130aacSRichard Genoud enum mctrl_gpio_idx i; 12184130aacSRichard Genoud 12284130aacSRichard Genoud gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); 12384130aacSRichard Genoud if (!gpios) 12484130aacSRichard Genoud return ERR_PTR(-ENOMEM); 12584130aacSRichard Genoud 12684130aacSRichard Genoud for (i = 0; i < UART_GPIO_MAX; i++) { 127d9948267SStefan Roese char *gpio_str; 128d9948267SStefan Roese bool present; 129d9948267SStefan Roese 130d9948267SStefan Roese /* Check if GPIO property exists and continue if not */ 131d9948267SStefan Roese gpio_str = kasprintf(GFP_KERNEL, "%s-gpios", 132d9948267SStefan Roese mctrl_gpios_desc[i].name); 133d9948267SStefan Roese if (!gpio_str) 134d9948267SStefan Roese continue; 135d9948267SStefan Roese 136d9948267SStefan Roese present = device_property_present(dev, gpio_str); 137d9948267SStefan Roese kfree(gpio_str); 138d9948267SStefan Roese if (!present) 139d9948267SStefan Roese continue; 14084130aacSRichard Genoud 1411d267ea6SUwe Kleine-König gpios->gpio[i] = 1421d267ea6SUwe Kleine-König devm_gpiod_get_index_optional(dev, 1431d267ea6SUwe Kleine-König mctrl_gpios_desc[i].name, 1444ad8e34dSAndy Shevchenko idx, 1454ad8e34dSAndy Shevchenko mctrl_gpios_desc[i].flags); 1461d267ea6SUwe Kleine-König 1471d267ea6SUwe Kleine-König if (IS_ERR(gpios->gpio[i])) 14813bc2bb9SFabio Estevam return ERR_CAST(gpios->gpio[i]); 14984130aacSRichard Genoud } 15084130aacSRichard Genoud 15184130aacSRichard Genoud return gpios; 15284130aacSRichard Genoud } 1537d8c70d8SUwe Kleine-König EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto); 15484130aacSRichard Genoud 155ce59e48fSUwe Kleine-König #define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS) 156ce59e48fSUwe Kleine-König static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) 157ce59e48fSUwe Kleine-König { 158ce59e48fSUwe Kleine-König struct mctrl_gpios *gpios = context; 159ce59e48fSUwe Kleine-König struct uart_port *port = gpios->port; 160ce59e48fSUwe Kleine-König u32 mctrl = gpios->mctrl_prev; 161ce59e48fSUwe Kleine-König u32 mctrl_diff; 162d11df618SYegor Yefremov unsigned long flags; 163ce59e48fSUwe Kleine-König 164ce59e48fSUwe Kleine-König mctrl_gpio_get(gpios, &mctrl); 165ce59e48fSUwe Kleine-König 166d11df618SYegor Yefremov spin_lock_irqsave(&port->lock, flags); 167d11df618SYegor Yefremov 168ce59e48fSUwe Kleine-König mctrl_diff = mctrl ^ gpios->mctrl_prev; 169ce59e48fSUwe Kleine-König gpios->mctrl_prev = mctrl; 170ce59e48fSUwe Kleine-König 171ce59e48fSUwe Kleine-König if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) { 172ce59e48fSUwe Kleine-König if ((mctrl_diff & mctrl) & TIOCM_RI) 173ce59e48fSUwe Kleine-König port->icount.rng++; 174ce59e48fSUwe Kleine-König 175ce59e48fSUwe Kleine-König if ((mctrl_diff & mctrl) & TIOCM_DSR) 176ce59e48fSUwe Kleine-König port->icount.dsr++; 177ce59e48fSUwe Kleine-König 178ce59e48fSUwe Kleine-König if (mctrl_diff & TIOCM_CD) 179ce59e48fSUwe Kleine-König uart_handle_dcd_change(port, mctrl & TIOCM_CD); 180ce59e48fSUwe Kleine-König 181ce59e48fSUwe Kleine-König if (mctrl_diff & TIOCM_CTS) 182ce59e48fSUwe Kleine-König uart_handle_cts_change(port, mctrl & TIOCM_CTS); 183ce59e48fSUwe Kleine-König 184ce59e48fSUwe Kleine-König wake_up_interruptible(&port->state->port.delta_msr_wait); 185ce59e48fSUwe Kleine-König } 186ce59e48fSUwe Kleine-König 187d11df618SYegor Yefremov spin_unlock_irqrestore(&port->lock, flags); 188d11df618SYegor Yefremov 189ce59e48fSUwe Kleine-König return IRQ_HANDLED; 190ce59e48fSUwe Kleine-König } 191ce59e48fSUwe Kleine-König 192ce59e48fSUwe Kleine-König struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) 193ce59e48fSUwe Kleine-König { 194ce59e48fSUwe Kleine-König struct mctrl_gpios *gpios; 195ce59e48fSUwe Kleine-König enum mctrl_gpio_idx i; 196ce59e48fSUwe Kleine-König 197ce59e48fSUwe Kleine-König gpios = mctrl_gpio_init_noauto(port->dev, idx); 198ce59e48fSUwe Kleine-König if (IS_ERR(gpios)) 199ce59e48fSUwe Kleine-König return gpios; 200ce59e48fSUwe Kleine-König 201ce59e48fSUwe Kleine-König gpios->port = port; 202ce59e48fSUwe Kleine-König 203ce59e48fSUwe Kleine-König for (i = 0; i < UART_GPIO_MAX; ++i) { 204ce59e48fSUwe Kleine-König int ret; 205ce59e48fSUwe Kleine-König 2064ad8e34dSAndy Shevchenko if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i)) 207ce59e48fSUwe Kleine-König continue; 208ce59e48fSUwe Kleine-König 209ce59e48fSUwe Kleine-König ret = gpiod_to_irq(gpios->gpio[i]); 210*cbd90e74SYueHaibing if (ret < 0) { 211ce59e48fSUwe Kleine-König dev_err(port->dev, 212ce59e48fSUwe Kleine-König "failed to find corresponding irq for %s (idx=%d, err=%d)\n", 213ce59e48fSUwe Kleine-König mctrl_gpios_desc[i].name, idx, ret); 214ce59e48fSUwe Kleine-König return ERR_PTR(ret); 215ce59e48fSUwe Kleine-König } 216ce59e48fSUwe Kleine-König gpios->irq[i] = ret; 217ce59e48fSUwe Kleine-König 218ce59e48fSUwe Kleine-König /* irqs should only be enabled in .enable_ms */ 219ce59e48fSUwe Kleine-König irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN); 220ce59e48fSUwe Kleine-König 221ce59e48fSUwe Kleine-König ret = devm_request_irq(port->dev, gpios->irq[i], 222ce59e48fSUwe Kleine-König mctrl_gpio_irq_handle, 223ce59e48fSUwe Kleine-König IRQ_TYPE_EDGE_BOTH, dev_name(port->dev), 224ce59e48fSUwe Kleine-König gpios); 225ce59e48fSUwe Kleine-König if (ret) { 226ce59e48fSUwe Kleine-König /* alternatively implement polling */ 227ce59e48fSUwe Kleine-König dev_err(port->dev, 228ce59e48fSUwe Kleine-König "failed to request irq for %s (idx=%d, err=%d)\n", 229ce59e48fSUwe Kleine-König mctrl_gpios_desc[i].name, idx, ret); 230ce59e48fSUwe Kleine-König return ERR_PTR(ret); 231ce59e48fSUwe Kleine-König } 232ce59e48fSUwe Kleine-König } 233ce59e48fSUwe Kleine-König 234ce59e48fSUwe Kleine-König return gpios; 235ce59e48fSUwe Kleine-König } 2364f71a2e0SUwe Kleine-König EXPORT_SYMBOL_GPL(mctrl_gpio_init); 237ce59e48fSUwe Kleine-König 23884130aacSRichard Genoud void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) 23984130aacSRichard Genoud { 24084130aacSRichard Genoud enum mctrl_gpio_idx i; 24184130aacSRichard Genoud 242434be0aeSYegor Yefremov if (gpios == NULL) 243434be0aeSYegor Yefremov return; 244434be0aeSYegor Yefremov 245ce59e48fSUwe Kleine-König for (i = 0; i < UART_GPIO_MAX; i++) { 246ce59e48fSUwe Kleine-König if (gpios->irq[i]) 247ce59e48fSUwe Kleine-König devm_free_irq(gpios->port->dev, gpios->irq[i], gpios); 248ce59e48fSUwe Kleine-König 249445df7ffSUwe Kleine-König if (gpios->gpio[i]) 25084130aacSRichard Genoud devm_gpiod_put(dev, gpios->gpio[i]); 251ce59e48fSUwe Kleine-König } 25284130aacSRichard Genoud devm_kfree(dev, gpios); 25384130aacSRichard Genoud } 25484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_free); 255ce59e48fSUwe Kleine-König 256ce59e48fSUwe Kleine-König void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) 257ce59e48fSUwe Kleine-König { 258ce59e48fSUwe Kleine-König enum mctrl_gpio_idx i; 259ce59e48fSUwe Kleine-König 260434be0aeSYegor Yefremov if (gpios == NULL) 261434be0aeSYegor Yefremov return; 262434be0aeSYegor Yefremov 263ce59e48fSUwe Kleine-König /* .enable_ms may be called multiple times */ 264ce59e48fSUwe Kleine-König if (gpios->mctrl_on) 265ce59e48fSUwe Kleine-König return; 266ce59e48fSUwe Kleine-König 267ce59e48fSUwe Kleine-König gpios->mctrl_on = true; 268ce59e48fSUwe Kleine-König 269ce59e48fSUwe Kleine-König /* get initial status of modem lines GPIOs */ 270ce59e48fSUwe Kleine-König mctrl_gpio_get(gpios, &gpios->mctrl_prev); 271ce59e48fSUwe Kleine-König 272ce59e48fSUwe Kleine-König for (i = 0; i < UART_GPIO_MAX; ++i) { 273ce59e48fSUwe Kleine-König if (!gpios->irq[i]) 274ce59e48fSUwe Kleine-König continue; 275ce59e48fSUwe Kleine-König 276ce59e48fSUwe Kleine-König enable_irq(gpios->irq[i]); 277ce59e48fSUwe Kleine-König } 278ce59e48fSUwe Kleine-König } 279ce59e48fSUwe Kleine-König EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms); 280ce59e48fSUwe Kleine-König 281ce59e48fSUwe Kleine-König void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) 282ce59e48fSUwe Kleine-König { 283ce59e48fSUwe Kleine-König enum mctrl_gpio_idx i; 284ce59e48fSUwe Kleine-König 285434be0aeSYegor Yefremov if (gpios == NULL) 286434be0aeSYegor Yefremov return; 287434be0aeSYegor Yefremov 288ce59e48fSUwe Kleine-König if (!gpios->mctrl_on) 289ce59e48fSUwe Kleine-König return; 290ce59e48fSUwe Kleine-König 291ce59e48fSUwe Kleine-König gpios->mctrl_on = false; 292ce59e48fSUwe Kleine-König 293ce59e48fSUwe Kleine-König for (i = 0; i < UART_GPIO_MAX; ++i) { 294ce59e48fSUwe Kleine-König if (!gpios->irq[i]) 295ce59e48fSUwe Kleine-König continue; 296ce59e48fSUwe Kleine-König 297ce59e48fSUwe Kleine-König disable_irq(gpios->irq[i]); 298ce59e48fSUwe Kleine-König } 299ce59e48fSUwe Kleine-König } 3004f71a2e0SUwe Kleine-König EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms); 30182a3f87fSRomain Izard 30282a3f87fSRomain Izard MODULE_LICENSE("GPL"); 303