Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tom Burkart and committed by
Linus Torvalds
4c69add4 652e2218

+87 -3
+85 -3
drivers/pps/clients/pps-gpio.c
··· 35 35 #include <linux/list.h> 36 36 #include <linux/of_device.h> 37 37 #include <linux/of_gpio.h> 38 + #include <linux/timer.h> 39 + #include <linux/jiffies.h> 38 40 39 41 /* Info for each registered platform device */ 40 42 struct pps_gpio_device_data { ··· 44 42 struct pps_device *pps; /* PPS source device */ 45 43 struct pps_source_info info; /* PPS source information */ 46 44 struct gpio_desc *gpio_pin; /* GPIO port descriptors */ 45 + struct gpio_desc *echo_pin; 46 + struct timer_list echo_timer; /* timer to reset echo active state */ 47 47 bool assert_falling_edge; 48 48 bool capture_clear; 49 + unsigned int echo_active_ms; /* PPS echo active duration */ 50 + unsigned long echo_timeout; /* timer timeout value in jiffies */ 49 51 }; 50 52 51 53 /* ··· 70 64 rising_edge = gpiod_get_value(info->gpio_pin); 71 65 if ((rising_edge && !info->assert_falling_edge) || 72 66 (!rising_edge && info->assert_falling_edge)) 73 - pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL); 67 + pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data); 74 68 else if (info->capture_clear && 75 69 ((rising_edge && info->assert_falling_edge) || 76 70 (!rising_edge && !info->assert_falling_edge))) 77 - pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL); 71 + pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data); 78 72 79 73 return IRQ_HANDLED; 74 + } 75 + 76 + /* This function will only be called when an ECHO GPIO is defined */ 77 + static void pps_gpio_echo(struct pps_device *pps, int event, void *data) 78 + { 79 + /* add_timer() needs to write into info->echo_timer */ 80 + struct pps_gpio_device_data *info = data; 81 + 82 + switch (event) { 83 + case PPS_CAPTUREASSERT: 84 + if (pps->params.mode & PPS_ECHOASSERT) 85 + gpiod_set_value(info->echo_pin, 1); 86 + break; 87 + 88 + case PPS_CAPTURECLEAR: 89 + if (pps->params.mode & PPS_ECHOCLEAR) 90 + gpiod_set_value(info->echo_pin, 1); 91 + break; 92 + } 93 + 94 + /* fire the timer */ 95 + if (info->pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) { 96 + info->echo_timer.expires = jiffies + info->echo_timeout; 97 + add_timer(&info->echo_timer); 98 + } 99 + } 100 + 101 + /* Timer callback to reset the echo pin to the inactive state */ 102 + static void pps_gpio_echo_timer_callback(struct timer_list *t) 103 + { 104 + const struct pps_gpio_device_data *info; 105 + 106 + info = from_timer(info, t, echo_timer); 107 + 108 + gpiod_set_value(info->echo_pin, 0); 80 109 } 81 110 82 111 static int pps_gpio_setup(struct platform_device *pdev) 83 112 { 84 113 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 85 114 struct device_node *np = pdev->dev.of_node; 115 + int ret; 116 + u32 value; 86 117 87 118 data->gpio_pin = devm_gpiod_get(&pdev->dev, 88 119 NULL, /* request "gpios" */ ··· 128 85 dev_err(&pdev->dev, 129 86 "failed to request PPS GPIO\n"); 130 87 return PTR_ERR(data->gpio_pin); 88 + } 89 + 90 + data->echo_pin = devm_gpiod_get_optional(&pdev->dev, 91 + "echo", 92 + GPIOD_OUT_LOW); 93 + if (data->echo_pin) { 94 + if (IS_ERR(data->echo_pin)) { 95 + dev_err(&pdev->dev, "failed to request ECHO GPIO\n"); 96 + return PTR_ERR(data->echo_pin); 97 + } 98 + 99 + ret = of_property_read_u32(np, 100 + "echo-active-ms", 101 + &value); 102 + if (ret) { 103 + dev_err(&pdev->dev, 104 + "failed to get echo-active-ms from OF\n"); 105 + return ret; 106 + } 107 + data->echo_active_ms = value; 108 + /* sanity check on echo_active_ms */ 109 + if (!data->echo_active_ms || data->echo_active_ms > 999) { 110 + dev_err(&pdev->dev, 111 + "echo-active-ms: %u - bad value from OF\n", 112 + data->echo_active_ms); 113 + return -EINVAL; 114 + } 131 115 } 132 116 133 117 if (of_property_read_bool(np, "assert-falling-edge")) ··· 192 122 /* GPIO setup */ 193 123 if (pdata) { 194 124 data->gpio_pin = pdata->gpio_pin; 125 + data->echo_pin = pdata->echo_pin; 195 126 196 127 data->assert_falling_edge = pdata->assert_falling_edge; 197 128 data->capture_clear = pdata->capture_clear; 129 + data->echo_active_ms = pdata->echo_active_ms; 198 130 } else { 199 131 ret = pps_gpio_setup(pdev); 200 132 if (ret) ··· 220 148 data->info.owner = THIS_MODULE; 221 149 snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 222 150 pdev->name, pdev->id); 151 + if (data->echo_pin) { 152 + data->info.echo = pps_gpio_echo; 153 + data->echo_timeout = msecs_to_jiffies(data->echo_active_ms); 154 + timer_setup(&data->echo_timer, pps_gpio_echo_timer_callback, 0); 155 + } 223 156 224 157 /* register PPS source */ 225 158 pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; ··· 257 180 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 258 181 259 182 pps_unregister_source(data->pps); 183 + if (data->echo_pin) { 184 + del_timer_sync(&data->echo_timer); 185 + /* reset echo pin in any case */ 186 + gpiod_set_value(data->echo_pin, 0); 187 + } 260 188 dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq); 261 189 return 0; 262 190 } ··· 286 204 MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 287 205 MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 288 206 MODULE_LICENSE("GPL"); 289 - MODULE_VERSION("1.1.0"); 207 + MODULE_VERSION("1.2.0");
+2
include/linux/pps-gpio.h
··· 24 24 25 25 struct pps_gpio_platform_data { 26 26 struct gpio_desc *gpio_pin; 27 + struct gpio_desc *echo_pin; 27 28 bool assert_falling_edge; 28 29 bool capture_clear; 30 + unsigned int echo_active_ms; 29 31 }; 30 32 31 33 #endif /* _PPS_GPIO_H */