LCOV - code coverage report
Current view: top level - drivers/tty/serial - serial_mctrl_gpio.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 0 108 0.0 %
Date: 2020-09-30 20:25:40 Functions: 0 10 0.0 %
Branches: 0 88 0.0 %

           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");

Generated by: LCOV version 1.14