Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+ 2 : : /* 3 : : * Helpers for controlling modem lines via GPIO 4 : : * 5 : : * Copyright (C) 2014 Paratronic S.A. 6 : : */ 7 : : 8 : : #include <linux/err.h> 9 : : #include <linux/device.h> 10 : : #include <linux/irq.h> 11 : : #include <linux/gpio/consumer.h> 12 : : #include <linux/termios.h> 13 : : #include <linux/serial_core.h> 14 : : #include <linux/module.h> 15 : : #include <linux/property.h> 16 : : 17 : : #include "serial_mctrl_gpio.h" 18 : : 19 : : struct mctrl_gpios { 20 : : struct uart_port *port; 21 : : struct gpio_desc *gpio[UART_GPIO_MAX]; 22 : : int irq[UART_GPIO_MAX]; 23 : : unsigned int mctrl_prev; 24 : : bool mctrl_on; 25 : : }; 26 : : 27 : : static const struct { 28 : : const char *name; 29 : : unsigned int mctrl; 30 : : enum gpiod_flags flags; 31 : : } mctrl_gpios_desc[UART_GPIO_MAX] = { 32 : : { "cts", TIOCM_CTS, GPIOD_IN, }, 33 : : { "dsr", TIOCM_DSR, GPIOD_IN, }, 34 : : { "dcd", TIOCM_CD, GPIOD_IN, }, 35 : : { "rng", TIOCM_RNG, GPIOD_IN, }, 36 : : { "rts", TIOCM_RTS, GPIOD_OUT_LOW, }, 37 : : { "dtr", TIOCM_DTR, GPIOD_OUT_LOW, }, 38 : : }; 39 : : 40 : : static bool mctrl_gpio_flags_is_dir_out(unsigned int idx) 41 : : { 42 : 0 : return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT; 43 : : } 44 : : 45 : 0 : void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) 46 : : { 47 : : enum mctrl_gpio_idx i; 48 : : struct gpio_desc *desc_array[UART_GPIO_MAX]; 49 : : DECLARE_BITMAP(values, UART_GPIO_MAX); 50 : : unsigned int count = 0; 51 : : 52 : 0 : if (gpios == NULL) 53 : 0 : return; 54 : : 55 : 0 : for (i = 0; i < UART_GPIO_MAX; i++) 56 : 0 : if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) { 57 : 0 : desc_array[count] = gpios->gpio[i]; 58 : 0 : __assign_bit(count, values, 59 : 0 : mctrl & mctrl_gpios_desc[i].mctrl); 60 : 0 : count++; 61 : : } 62 : 0 : gpiod_set_array_value(count, desc_array, NULL, values); 63 : : } 64 : : EXPORT_SYMBOL_GPL(mctrl_gpio_set); 65 : : 66 : 0 : struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, 67 : : enum mctrl_gpio_idx gidx) 68 : : { 69 : 0 : if (gpios == NULL) 70 : : return NULL; 71 : : 72 : 0 : return gpios->gpio[gidx]; 73 : : } 74 : : EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); 75 : : 76 : 0 : unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) 77 : : { 78 : : enum mctrl_gpio_idx i; 79 : : 80 : 0 : if (gpios == NULL) 81 : 0 : return *mctrl; 82 : : 83 : 0 : for (i = 0; i < UART_GPIO_MAX; i++) { 84 : 0 : if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) { 85 : 0 : if (gpiod_get_value(gpios->gpio[i])) 86 : 0 : *mctrl |= mctrl_gpios_desc[i].mctrl; 87 : : else 88 : 0 : *mctrl &= ~mctrl_gpios_desc[i].mctrl; 89 : : } 90 : : } 91 : : 92 : 0 : return *mctrl; 93 : : } 94 : : EXPORT_SYMBOL_GPL(mctrl_gpio_get); 95 : : 96 : : unsigned int 97 : 0 : mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl) 98 : : { 99 : : enum mctrl_gpio_idx i; 100 : : 101 : 0 : if (gpios == NULL) 102 : 0 : return *mctrl; 103 : : 104 : 0 : for (i = 0; i < UART_GPIO_MAX; i++) { 105 : 0 : if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) { 106 : 0 : if (gpiod_get_value(gpios->gpio[i])) 107 : 0 : *mctrl |= mctrl_gpios_desc[i].mctrl; 108 : : else 109 : 0 : *mctrl &= ~mctrl_gpios_desc[i].mctrl; 110 : : } 111 : : } 112 : : 113 : 0 : return *mctrl; 114 : : } 115 : : EXPORT_SYMBOL_GPL(mctrl_gpio_get_outputs); 116 : : 117 : 0 : struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) 118 : : { 119 : : struct mctrl_gpios *gpios; 120 : : enum mctrl_gpio_idx i; 121 : : 122 : : gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); 123 : 0 : if (!gpios) 124 : : return ERR_PTR(-ENOMEM); 125 : : 126 : 0 : for (i = 0; i < UART_GPIO_MAX; i++) { 127 : : char *gpio_str; 128 : : bool present; 129 : : 130 : : /* Check if GPIO property exists and continue if not */ 131 : 0 : gpio_str = kasprintf(GFP_KERNEL, "%s-gpios", 132 : : mctrl_gpios_desc[i].name); 133 : 0 : if (!gpio_str) 134 : 0 : continue; 135 : : 136 : 0 : present = device_property_present(dev, gpio_str); 137 : 0 : kfree(gpio_str); 138 : 0 : if (!present) 139 : 0 : continue; 140 : : 141 : 0 : gpios->gpio[i] = 142 : 0 : devm_gpiod_get_index_optional(dev, 143 : : mctrl_gpios_desc[i].name, 144 : : idx, 145 : : mctrl_gpios_desc[i].flags); 146 : : 147 : 0 : if (IS_ERR(gpios->gpio[i])) 148 : 0 : return ERR_CAST(gpios->gpio[i]); 149 : : } 150 : : 151 : : return gpios; 152 : : } 153 : : EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto); 154 : : 155 : : #define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS) 156 : 0 : static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) 157 : : { 158 : : struct mctrl_gpios *gpios = context; 159 : 0 : struct uart_port *port = gpios->port; 160 : 0 : u32 mctrl = gpios->mctrl_prev; 161 : : u32 mctrl_diff; 162 : : unsigned long flags; 163 : : 164 : 0 : mctrl_gpio_get(gpios, &mctrl); 165 : : 166 : 0 : spin_lock_irqsave(&port->lock, flags); 167 : : 168 : 0 : mctrl_diff = mctrl ^ gpios->mctrl_prev; 169 : 0 : gpios->mctrl_prev = mctrl; 170 : : 171 : 0 : if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) { 172 : 0 : if ((mctrl_diff & mctrl) & TIOCM_RI) 173 : 0 : port->icount.rng++; 174 : : 175 : 0 : if ((mctrl_diff & mctrl) & TIOCM_DSR) 176 : 0 : port->icount.dsr++; 177 : : 178 : 0 : if (mctrl_diff & TIOCM_CD) 179 : 0 : uart_handle_dcd_change(port, mctrl & TIOCM_CD); 180 : : 181 : 0 : if (mctrl_diff & TIOCM_CTS) 182 : 0 : uart_handle_cts_change(port, mctrl & TIOCM_CTS); 183 : : 184 : 0 : wake_up_interruptible(&port->state->port.delta_msr_wait); 185 : : } 186 : : 187 : : spin_unlock_irqrestore(&port->lock, flags); 188 : : 189 : 0 : return IRQ_HANDLED; 190 : : } 191 : : 192 : 0 : struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) 193 : : { 194 : : struct mctrl_gpios *gpios; 195 : : enum mctrl_gpio_idx i; 196 : : 197 : 0 : gpios = mctrl_gpio_init_noauto(port->dev, idx); 198 : 0 : if (IS_ERR(gpios)) 199 : : return gpios; 200 : : 201 : 0 : gpios->port = port; 202 : : 203 : 0 : for (i = 0; i < UART_GPIO_MAX; ++i) { 204 : : int ret; 205 : : 206 : 0 : if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i)) 207 : 0 : continue; 208 : : 209 : 0 : ret = gpiod_to_irq(gpios->gpio[i]); 210 : 0 : if (ret <= 0) { 211 : 0 : dev_err(port->dev, 212 : : "failed to find corresponding irq for %s (idx=%d, err=%d)\n", 213 : : mctrl_gpios_desc[i].name, idx, ret); 214 : 0 : return ERR_PTR(ret); 215 : : } 216 : 0 : gpios->irq[i] = ret; 217 : : 218 : : /* irqs should only be enabled in .enable_ms */ 219 : 0 : irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN); 220 : : 221 : 0 : ret = devm_request_irq(port->dev, gpios->irq[i], 222 : : mctrl_gpio_irq_handle, 223 : 0 : IRQ_TYPE_EDGE_BOTH, dev_name(port->dev), 224 : : gpios); 225 : 0 : if (ret) { 226 : : /* alternatively implement polling */ 227 : 0 : dev_err(port->dev, 228 : : "failed to request irq for %s (idx=%d, err=%d)\n", 229 : : mctrl_gpios_desc[i].name, idx, ret); 230 : 0 : return ERR_PTR(ret); 231 : : } 232 : : } 233 : : 234 : : return gpios; 235 : : } 236 : : EXPORT_SYMBOL_GPL(mctrl_gpio_init); 237 : : 238 : 0 : void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) 239 : : { 240 : : enum mctrl_gpio_idx i; 241 : : 242 : 0 : if (gpios == NULL) 243 : 0 : return; 244 : : 245 : 0 : for (i = 0; i < UART_GPIO_MAX; i++) { 246 : 0 : if (gpios->irq[i]) 247 : 0 : devm_free_irq(gpios->port->dev, gpios->irq[i], gpios); 248 : : 249 : 0 : if (gpios->gpio[i]) 250 : 0 : devm_gpiod_put(dev, gpios->gpio[i]); 251 : : } 252 : 0 : devm_kfree(dev, gpios); 253 : : } 254 : : EXPORT_SYMBOL_GPL(mctrl_gpio_free); 255 : : 256 : 0 : void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) 257 : : { 258 : : enum mctrl_gpio_idx i; 259 : : 260 : 0 : if (gpios == NULL) 261 : : return; 262 : : 263 : : /* .enable_ms may be called multiple times */ 264 : 0 : if (gpios->mctrl_on) 265 : : return; 266 : : 267 : 0 : gpios->mctrl_on = true; 268 : : 269 : : /* get initial status of modem lines GPIOs */ 270 : 0 : mctrl_gpio_get(gpios, &gpios->mctrl_prev); 271 : : 272 : 0 : for (i = 0; i < UART_GPIO_MAX; ++i) { 273 : 0 : if (!gpios->irq[i]) 274 : 0 : continue; 275 : : 276 : 0 : enable_irq(gpios->irq[i]); 277 : : } 278 : : } 279 : : EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms); 280 : : 281 : 0 : void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) 282 : : { 283 : : enum mctrl_gpio_idx i; 284 : : 285 : 0 : if (gpios == NULL) 286 : : return; 287 : : 288 : 0 : if (!gpios->mctrl_on) 289 : : return; 290 : : 291 : 0 : gpios->mctrl_on = false; 292 : : 293 : 0 : for (i = 0; i < UART_GPIO_MAX; ++i) { 294 : 0 : if (!gpios->irq[i]) 295 : 0 : continue; 296 : : 297 : 0 : disable_irq(gpios->irq[i]); 298 : : } 299 : : } 300 : : EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms); 301 : : 302 : : MODULE_LICENSE("GPL");