Linux GPIO PPS echo patch
The snippet can be accessed without any authentication.
Authored by
Lukas Senger
ppsecho.patch 5.39 KiB
diff --git a/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts b/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts
index 9ee4bdfa6167..bc299f581868 100644
--- a/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts
+++ b/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts
@@ -11,6 +11,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pps_pins>;
gpios = <&gpio 18 0>;
+ echo-gpios = <&gpio 17 0>;
status = "okay";
};
};
@@ -20,9 +21,9 @@
target = <&gpio>;
__overlay__ {
pps_pins: pps_pins@12 {
- brcm,pins = <18>;
- brcm,function = <0>; // in
- brcm,pull = <0>; // off
+ brcm,pins = <18 17>;
+ brcm,function = <0 1>; // in out
+ brcm,pull = <0 0>; // off off
};
};
};
@@ -32,6 +33,8 @@
<&pps>,"reg:0",
<&pps_pins>,"brcm,pins:0",
<&pps_pins>,"reg:0";
+ echopin = <&pps>,"echo-gpios:4",
+ <&pps_pins>,"brcm,pins:4";
assert_falling_edge = <&pps>,"assert-falling-edge?";
};
};
diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c
index 333ad7d5b45b..08f2ebdab5e5 100644
--- a/drivers/pps/clients/pps-gpio.c
+++ b/drivers/pps/clients/pps-gpio.c
@@ -35,6 +35,8 @@
#include <linux/list.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
+#include <linux/delay.h>
+
/* Info for each registered platform device */
struct pps_gpio_device_data {
@@ -44,6 +46,7 @@ struct pps_gpio_device_data {
bool assert_falling_edge;
bool capture_clear;
unsigned int gpio_pin;
+ unsigned int echo_pin;
};
/*
@@ -64,15 +67,46 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
rising_edge = gpio_get_value(info->gpio_pin);
if ((rising_edge && !info->assert_falling_edge) ||
(!rising_edge && info->assert_falling_edge))
- pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
+ pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
else if (info->capture_clear &&
((rising_edge && info->assert_falling_edge) ||
(!rising_edge && !info->assert_falling_edge)))
- pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
+ pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
+
+ if (info->pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))
+ return IRQ_WAKE_THREAD;
+ return IRQ_HANDLED;
+}
+
+static void pps_gpio_echo(struct pps_device *pps, int event, void *data)
+{
+ const struct pps_gpio_device_data *info;
+
+ info = data;
+
+ if (event == PPS_CAPTUREASSERT && (pps->params.mode & PPS_ECHOASSERT))
+ gpio_set_value(info->echo_pin, 1);
+ else if (event == PPS_CAPTURECLEAR && (pps->params.mode & PPS_ECHOCLEAR))
+ gpio_set_value(info->echo_pin, 1);
+}
+
+
+/* If we sent an echo pulse, we set the output pin back to low. This results in
+ * an echo pulse of roughly 100ms length.
+ */
+static irqreturn_t pps_gpio_irq_threaded(int irq, void *data)
+{
+ const struct pps_gpio_device_data *info;
+
+ info = data;
+
+ msleep(100);
+ gpio_set_value(info->echo_pin, 0);
return IRQ_HANDLED;
}
+
static unsigned long
get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
{
@@ -104,17 +138,24 @@ static int pps_gpio_probe(struct platform_device *pdev)
if (pdata) {
data->gpio_pin = pdata->gpio_pin;
+ data->echo_pin = pdata->echo_pin;
gpio_label = pdata->gpio_label;
data->assert_falling_edge = pdata->assert_falling_edge;
data->capture_clear = pdata->capture_clear;
} else {
- ret = of_get_gpio(np, 0);
+ ret = of_get_named_gpio(np, "gpios", 0);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get GPIO from device tree\n");
return ret;
}
data->gpio_pin = ret;
+ ret = of_get_named_gpio(np, "echo-gpios", 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get second GPIO from device tree\n");
+ return ret;
+ }
+ data->echo_pin = ret;
gpio_label = PPS_GPIO_NAME;
if (of_get_property(np, "assert-falling-edge", NULL))
@@ -131,7 +172,20 @@ static int pps_gpio_probe(struct platform_device *pdev)
ret = gpio_direction_input(data->gpio_pin);
if (ret) {
- dev_err(&pdev->dev, "failed to set pin direction\n");
+ dev_err(&pdev->dev, "failed to set pin as input\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request(&pdev->dev, data->echo_pin, gpio_label);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request GPIO %u\n",
+ data->echo_pin);
+ return ret;
+ }
+
+ ret = gpio_direction_output(data->echo_pin, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pin as output\n");
return -EINVAL;
}
@@ -152,6 +206,7 @@ static int pps_gpio_probe(struct platform_device *pdev)
data->info.owner = THIS_MODULE;
snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
pdev->name, pdev->id);
+ data->info.echo = pps_gpio_echo;
/* register PPS source */
pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
@@ -165,7 +220,8 @@ static int pps_gpio_probe(struct platform_device *pdev)
}
/* register IRQ interrupt handler */
- ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler,
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq,
+ pps_gpio_irq_handler, pps_gpio_irq_threaded,
get_irqf_trigger_flags(data), data->info.name, data);
if (ret) {
pps_unregister_source(data->pps);
diff --git a/include/linux/pps-gpio.h b/include/linux/pps-gpio.h
index 0035abe41b9a..055edd43012a 100644
--- a/include/linux/pps-gpio.h
+++ b/include/linux/pps-gpio.h
@@ -26,6 +26,7 @@ struct pps_gpio_platform_data {
bool assert_falling_edge;
bool capture_clear;
unsigned int gpio_pin;
+ unsigned int echo_pin;
const char *gpio_label;
};
Please register or sign in to comment