Version:  2.0.40 2.2.26 2.4.37 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15

Linux/drivers/pps/clients/pps-gpio.c

  1 /*
  2  * pps-gpio.c -- PPS client driver using GPIO
  3  *
  4  *
  5  * Copyright (C) 2010 Ricardo Martins <rasm@fe.up.pt>
  6  * Copyright (C) 2011 James Nuss <jamesnuss@nanometrics.ca>
  7  *
  8  *   This program is free software; you can redistribute it and/or modify
  9  *   it under the terms of the GNU General Public License as published by
 10  *   the Free Software Foundation; either version 2 of the License, or
 11  *   (at your option) any later version.
 12  *
 13  *   This program is distributed in the hope that it will be useful,
 14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16  *   GNU General Public License for more details.
 17  *
 18  *   You should have received a copy of the GNU General Public License
 19  *   along with this program; if not, write to the Free Software
 20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 21  */
 22 
 23 #define PPS_GPIO_NAME "pps-gpio"
 24 #define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt
 25 
 26 #include <linux/init.h>
 27 #include <linux/kernel.h>
 28 #include <linux/interrupt.h>
 29 #include <linux/module.h>
 30 #include <linux/platform_device.h>
 31 #include <linux/slab.h>
 32 #include <linux/pps_kernel.h>
 33 #include <linux/pps-gpio.h>
 34 #include <linux/gpio.h>
 35 #include <linux/list.h>
 36 #include <linux/of_device.h>
 37 #include <linux/of_gpio.h>
 38 
 39 /* Info for each registered platform device */
 40 struct pps_gpio_device_data {
 41         int irq;                        /* IRQ used as PPS source */
 42         struct pps_device *pps;         /* PPS source device */
 43         struct pps_source_info info;    /* PPS source information */
 44         bool assert_falling_edge;
 45         bool capture_clear;
 46         unsigned int gpio_pin;
 47 };
 48 
 49 /*
 50  * Report the PPS event
 51  */
 52 
 53 static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
 54 {
 55         const struct pps_gpio_device_data *info;
 56         struct pps_event_time ts;
 57         int rising_edge;
 58 
 59         /* Get the time stamp first */
 60         pps_get_ts(&ts);
 61 
 62         info = data;
 63 
 64         rising_edge = gpio_get_value(info->gpio_pin);
 65         if ((rising_edge && !info->assert_falling_edge) ||
 66                         (!rising_edge && info->assert_falling_edge))
 67                 pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
 68         else if (info->capture_clear &&
 69                         ((rising_edge && info->assert_falling_edge) ||
 70                          (!rising_edge && !info->assert_falling_edge)))
 71                 pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
 72 
 73         return IRQ_HANDLED;
 74 }
 75 
 76 static unsigned long
 77 get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
 78 {
 79         unsigned long flags = data->assert_falling_edge ?
 80                 IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
 81 
 82         if (data->capture_clear) {
 83                 flags |= ((flags & IRQF_TRIGGER_RISING) ?
 84                                 IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
 85         }
 86 
 87         return flags;
 88 }
 89 
 90 static int pps_gpio_probe(struct platform_device *pdev)
 91 {
 92         struct pps_gpio_device_data *data;
 93         const char *gpio_label;
 94         int ret;
 95         int pps_default_params;
 96         const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
 97         struct device_node *np = pdev->dev.of_node;
 98 
 99         /* allocate space for device info */
100         data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data),
101                         GFP_KERNEL);
102         if (!data)
103                 return -ENOMEM;
104 
105         if (pdata) {
106                 data->gpio_pin = pdata->gpio_pin;
107                 gpio_label = pdata->gpio_label;
108 
109                 data->assert_falling_edge = pdata->assert_falling_edge;
110                 data->capture_clear = pdata->capture_clear;
111         } else {
112                 ret = of_get_gpio(np, 0);
113                 if (ret < 0) {
114                         dev_err(&pdev->dev, "failed to get GPIO from device tree\n");
115                         return ret;
116                 }
117                 data->gpio_pin = ret;
118                 gpio_label = PPS_GPIO_NAME;
119 
120                 if (of_get_property(np, "assert-falling-edge", NULL))
121                         data->assert_falling_edge = true;
122         }
123 
124         /* GPIO setup */
125         ret = devm_gpio_request(&pdev->dev, data->gpio_pin, gpio_label);
126         if (ret) {
127                 dev_err(&pdev->dev, "failed to request GPIO %u\n",
128                         data->gpio_pin);
129                 return ret;
130         }
131 
132         ret = gpio_direction_input(data->gpio_pin);
133         if (ret) {
134                 dev_err(&pdev->dev, "failed to set pin direction\n");
135                 return -EINVAL;
136         }
137 
138         /* IRQ setup */
139         ret = gpio_to_irq(data->gpio_pin);
140         if (ret < 0) {
141                 dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret);
142                 return -EINVAL;
143         }
144         data->irq = ret;
145 
146         /* initialize PPS specific parts of the bookkeeping data structure. */
147         data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
148                 PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
149         if (data->capture_clear)
150                 data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
151                         PPS_ECHOCLEAR;
152         data->info.owner = THIS_MODULE;
153         snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
154                  pdev->name, pdev->id);
155 
156         /* register PPS source */
157         pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
158         if (data->capture_clear)
159                 pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
160         data->pps = pps_register_source(&data->info, pps_default_params);
161         if (data->pps == NULL) {
162                 dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n",
163                         data->irq);
164                 return -EINVAL;
165         }
166 
167         /* register IRQ interrupt handler */
168         ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler,
169                         get_irqf_trigger_flags(data), data->info.name, data);
170         if (ret) {
171                 pps_unregister_source(data->pps);
172                 dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq);
173                 return -EINVAL;
174         }
175 
176         platform_set_drvdata(pdev, data);
177         dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n",
178                  data->irq);
179 
180         return 0;
181 }
182 
183 static int pps_gpio_remove(struct platform_device *pdev)
184 {
185         struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
186 
187         pps_unregister_source(data->pps);
188         dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq);
189         return 0;
190 }
191 
192 static const struct of_device_id pps_gpio_dt_ids[] = {
193         { .compatible = "pps-gpio", },
194         { /* sentinel */ }
195 };
196 MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids);
197 
198 static struct platform_driver pps_gpio_driver = {
199         .probe          = pps_gpio_probe,
200         .remove         = pps_gpio_remove,
201         .driver         = {
202                 .name   = PPS_GPIO_NAME,
203                 .owner  = THIS_MODULE,
204                 .of_match_table = pps_gpio_dt_ids,
205         },
206 };
207 
208 module_platform_driver(pps_gpio_driver);
209 MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
210 MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
211 MODULE_DESCRIPTION("Use GPIO pin as PPS source");
212 MODULE_LICENSE("GPL");
213 MODULE_VERSION("1.0.0");
214 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us