summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Burkart <tom@aussec.com>2019-05-14 18:45:46 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-14 22:52:51 -0400
commit4c69add45fec995ce23b21dc90be20f1efd8cdad (patch)
treeeaa128acbef1efe8456cdb72892549b03bef78f1
parent652e22185a4414143a1d17975bd9ef0b12ea7b37 (diff)
pps: pps-gpio PPS ECHO implementation
This patch implements the PPS ECHO functionality for pps-gpio, that sysfs claims is available already. Configuration is done via device tree bindings. No changes are made to userspace interfaces. This patch was originally written by Lukas Senger as part of a masters thesis project and modified for inclusion into the linux kernel by Tom Burkart. Link: http://lkml.kernel.org/r/20190324043305.6627-4-tom@aussec.com Signed-off-by: Tom Burkart <tom@aussec.com> Acked-by: Rodolfo Giometti <giometti@enneenne.com> Signed-off-by: Lukas Senger <lukas@fridolin.com> Cc: Philipp Zabel <philipp.zabel@gmail.com> Cc: Rob Herring <robh@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/pps/clients/pps-gpio.c88
-rw-r--r--include/linux/pps-gpio.h2
2 files changed, 87 insertions, 3 deletions
diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c
index 4e5e9229814b..4b6418039387 100644
--- a/drivers/pps/clients/pps-gpio.c
+++ b/drivers/pps/clients/pps-gpio.c
@@ -35,6 +35,8 @@
35#include <linux/list.h> 35#include <linux/list.h>
36#include <linux/of_device.h> 36#include <linux/of_device.h>
37#include <linux/of_gpio.h> 37#include <linux/of_gpio.h>
38#include <linux/timer.h>
39#include <linux/jiffies.h>
38 40
39/* Info for each registered platform device */ 41/* Info for each registered platform device */
40struct pps_gpio_device_data { 42struct pps_gpio_device_data {
@@ -42,8 +44,12 @@ struct pps_gpio_device_data {
42 struct pps_device *pps; /* PPS source device */ 44 struct pps_device *pps; /* PPS source device */
43 struct pps_source_info info; /* PPS source information */ 45 struct pps_source_info info; /* PPS source information */
44 struct gpio_desc *gpio_pin; /* GPIO port descriptors */ 46 struct gpio_desc *gpio_pin; /* GPIO port descriptors */
47 struct gpio_desc *echo_pin;
48 struct timer_list echo_timer; /* timer to reset echo active state */
45 bool assert_falling_edge; 49 bool assert_falling_edge;
46 bool capture_clear; 50 bool capture_clear;
51 unsigned int echo_active_ms; /* PPS echo active duration */
52 unsigned long echo_timeout; /* timer timeout value in jiffies */
47}; 53};
48 54
49/* 55/*
@@ -64,19 +70,56 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
64 rising_edge = gpiod_get_value(info->gpio_pin); 70 rising_edge = gpiod_get_value(info->gpio_pin);
65 if ((rising_edge && !info->assert_falling_edge) || 71 if ((rising_edge && !info->assert_falling_edge) ||
66 (!rising_edge && info->assert_falling_edge)) 72 (!rising_edge && info->assert_falling_edge))
67 pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL); 73 pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
68 else if (info->capture_clear && 74 else if (info->capture_clear &&
69 ((rising_edge && info->assert_falling_edge) || 75 ((rising_edge && info->assert_falling_edge) ||
70 (!rising_edge && !info->assert_falling_edge))) 76 (!rising_edge && !info->assert_falling_edge)))
71 pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL); 77 pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
72 78
73 return IRQ_HANDLED; 79 return IRQ_HANDLED;
74} 80}
75 81
82/* This function will only be called when an ECHO GPIO is defined */
83static void pps_gpio_echo(struct pps_device *pps, int event, void *data)
84{
85 /* add_timer() needs to write into info->echo_timer */
86 struct pps_gpio_device_data *info = data;
87
88 switch (event) {
89 case PPS_CAPTUREASSERT:
90 if (pps->params.mode & PPS_ECHOASSERT)
91 gpiod_set_value(info->echo_pin, 1);
92 break;
93
94 case PPS_CAPTURECLEAR:
95 if (pps->params.mode & PPS_ECHOCLEAR)
96 gpiod_set_value(info->echo_pin, 1);
97 break;
98 }
99
100 /* fire the timer */
101 if (info->pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) {
102 info->echo_timer.expires = jiffies + info->echo_timeout;
103 add_timer(&info->echo_timer);
104 }
105}
106
107/* Timer callback to reset the echo pin to the inactive state */
108static void pps_gpio_echo_timer_callback(struct timer_list *t)
109{
110 const struct pps_gpio_device_data *info;
111
112 info = from_timer(info, t, echo_timer);
113
114 gpiod_set_value(info->echo_pin, 0);
115}
116
76static int pps_gpio_setup(struct platform_device *pdev) 117static int pps_gpio_setup(struct platform_device *pdev)
77{ 118{
78 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 119 struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
79 struct device_node *np = pdev->dev.of_node; 120 struct device_node *np = pdev->dev.of_node;
121 int ret;
122 u32 value;
80 123
81 data->gpio_pin = devm_gpiod_get(&pdev->dev, 124 data->gpio_pin = devm_gpiod_get(&pdev->dev,
82 NULL, /* request "gpios" */ 125 NULL, /* request "gpios" */
@@ -87,6 +130,33 @@ static int pps_gpio_setup(struct platform_device *pdev)
87 return PTR_ERR(data->gpio_pin); 130 return PTR_ERR(data->gpio_pin);
88 } 131 }
89 132
133 data->echo_pin = devm_gpiod_get_optional(&pdev->dev,
134 "echo",
135 GPIOD_OUT_LOW);
136 if (data->echo_pin) {
137 if (IS_ERR(data->echo_pin)) {
138 dev_err(&pdev->dev, "failed to request ECHO GPIO\n");
139 return PTR_ERR(data->echo_pin);
140 }
141
142 ret = of_property_read_u32(np,
143 "echo-active-ms",
144 &value);
145 if (ret) {
146 dev_err(&pdev->dev,
147 "failed to get echo-active-ms from OF\n");
148 return ret;
149 }
150 data->echo_active_ms = value;
151 /* sanity check on echo_active_ms */
152 if (!data->echo_active_ms || data->echo_active_ms > 999) {
153 dev_err(&pdev->dev,
154 "echo-active-ms: %u - bad value from OF\n",
155 data->echo_active_ms);
156 return -EINVAL;
157 }
158 }
159
90 if (of_property_read_bool(np, "assert-falling-edge")) 160 if (of_property_read_bool(np, "assert-falling-edge"))
91 data->assert_falling_edge = true; 161 data->assert_falling_edge = true;
92 return 0; 162 return 0;
@@ -122,9 +192,11 @@ static int pps_gpio_probe(struct platform_device *pdev)
122 /* GPIO setup */ 192 /* GPIO setup */
123 if (pdata) { 193 if (pdata) {
124 data->gpio_pin = pdata->gpio_pin; 194 data->gpio_pin = pdata->gpio_pin;
195 data->echo_pin = pdata->echo_pin;
125 196
126 data->assert_falling_edge = pdata->assert_falling_edge; 197 data->assert_falling_edge = pdata->assert_falling_edge;
127 data->capture_clear = pdata->capture_clear; 198 data->capture_clear = pdata->capture_clear;
199 data->echo_active_ms = pdata->echo_active_ms;
128 } else { 200 } else {
129 ret = pps_gpio_setup(pdev); 201 ret = pps_gpio_setup(pdev);
130 if (ret) 202 if (ret)
@@ -148,6 +220,11 @@ static int pps_gpio_probe(struct platform_device *pdev)
148 data->info.owner = THIS_MODULE; 220 data->info.owner = THIS_MODULE;
149 snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 221 snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
150 pdev->name, pdev->id); 222 pdev->name, pdev->id);
223 if (data->echo_pin) {
224 data->info.echo = pps_gpio_echo;
225 data->echo_timeout = msecs_to_jiffies(data->echo_active_ms);
226 timer_setup(&data->echo_timer, pps_gpio_echo_timer_callback, 0);
227 }
151 228
152 /* register PPS source */ 229 /* register PPS source */
153 pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 230 pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
@@ -180,6 +257,11 @@ static int pps_gpio_remove(struct platform_device *pdev)
180 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 257 struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
181 258
182 pps_unregister_source(data->pps); 259 pps_unregister_source(data->pps);
260 if (data->echo_pin) {
261 del_timer_sync(&data->echo_timer);
262 /* reset echo pin in any case */
263 gpiod_set_value(data->echo_pin, 0);
264 }
183 dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq); 265 dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq);
184 return 0; 266 return 0;
185} 267}
@@ -204,4 +286,4 @@ MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
204MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 286MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
205MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 287MODULE_DESCRIPTION("Use GPIO pin as PPS source");
206MODULE_LICENSE("GPL"); 288MODULE_LICENSE("GPL");
207MODULE_VERSION("1.1.0"); 289MODULE_VERSION("1.2.0");
diff --git a/include/linux/pps-gpio.h b/include/linux/pps-gpio.h
index f028d2cda6f5..44171e6b7197 100644
--- a/include/linux/pps-gpio.h
+++ b/include/linux/pps-gpio.h
@@ -24,8 +24,10 @@
24 24
25struct pps_gpio_platform_data { 25struct pps_gpio_platform_data {
26 struct gpio_desc *gpio_pin; 26 struct gpio_desc *gpio_pin;
27 struct gpio_desc *echo_pin;
27 bool assert_falling_edge; 28 bool assert_falling_edge;
28 bool capture_clear; 29 bool capture_clear;
30 unsigned int echo_active_ms;
29}; 31};
30 32
31#endif /* _PPS_GPIO_H */ 33#endif /* _PPS_GPIO_H */