PPS: pps-gpio PPS ECHO implementation
Subsystems (2)
v14 cover PPS: pps-gpio PPS ECHO implementation
Hi all,
please find attached the PPS-GPIO PPS ECHO implementation patch. The driver claims to have echo functionality in the sysfs interface but this functionality is not present. This patch provides this functionality.
Part 1 of the patch change the original driver from the number based GPIO API to the descriptor based API.
Parts 2 and 3 then add the PPS ECHO functionality. This is enabled if a "echo-gpios" entry is found in the devicetree.
Changes in v10:
Changes requested by Philipp Zabel:
Mostly cosmetic changes: PATCH 2/4 now reviewed. Thanks a lot, Philipp! (Please note that as of v11 PATCH 1 is gone so this has become PATCH 1/3)
Change in v11:
Change requested by Rob Herring:
All changes in regard to the capture-clear DT entry are gone.
Change in v12:
Change requested by Rob Herring:
Deleted superfluous use of invert-pps-echo
Change in v13:
Patch 2/3 (DT bindings) now reviewed. Thanks a lot, Rob!
Changes in v14:
Simplify code enabling PPS ECHO functionality.
On the linuxpps mailing list it was suggested to use a hrtimer for resetting the GPIO ECHO active state to the inactive state. Please also comment on whether a hrtimer is necessary/desirable for the purpose of resetting the echo pin active state. I am happy to implement it if this is useful/desirable.
Please install, test and comment as it is now a quite major change to the driver.
Suggestions for improvement are welcome.
Tom Burkart
Tom Burkart (3):
pps: descriptor-based gpio
dt-bindings: pps: pps-gpio PPS ECHO implementation
pps: pps-gpio pps-echo implementation
Documentation/devicetree/bindings/pps/pps-gpio.txt | 7 + drivers/pps/clients/pps-gpio.c | 154 ++++++++++++++++----- include/linux/pps-gpio.h | 5 +- 3 files changed, 126 insertions(+), 40 deletions(-)
Signature · 1 line
2.12.3
PATCH v14 1/3 pps: descriptor-based gpio
This patch changes the GPIO access for the pps-gpio driver from the integer based API to the descriptor based API.
--- drivers/pps/clients/pps-gpio.c | 67 +++++++++++++++++++----------------------- include/linux/pps-gpio.h | 3 +- 2 files changed, 32 insertions(+), 38 deletions(-)
drivers/pps/clients/pps-gpio.c +31 -36
@@ -31,7 +31,7 @@#include <linux/slab.h> #include <linux/pps_kernel.h> #include <linux/pps-gpio.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/list.h> #include <linux/of_device.h> #include <linux/of_gpio.h>
@@ -41,9 +41,9 @@ struct pps_gpio_device_data {int irq;/* IRQ used as PPS source */ struct pps_device *pps;/* PPS source device */ struct pps_source_info info;/* PPS source information */ +struct gpio_desc *gpio_pin;/* GPIO port descriptors */ bool assert_falling_edge;bool capture_clear; -unsigned int gpio_pin; };/*
@@ -61,18 +61,37 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) info = data; - rising_edge =gpio_get_value (info->gpio_pin); + rising_edge =gpiod_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 );else if (info->capture_clear && ((rising_edge && info->assert_falling_edge) || - (!rising_edge && !info->assert_falling_edge))) + (!rising_edge && !info->assert_falling_edge)))pps_event (info->pps, &ts,PPS_CAPTURECLEAR ,NULL );return IRQ_HANDLED ; } +static int pps_gpio_setup (struct platform_device *pdev) +{ +struct pps_gpio_device_data *data =platform_get_drvdata (pdev); +struct device_node *np = pdev->dev.of_node; + + data->gpio_pin =devm_gpiod_get (&pdev->dev, +NULL ,/* request "gpios" */ +GPIOD_IN ); +if (IS_ERR (data->gpio_pin)) { +dev_err (&pdev->dev, +"failed to request PPS GPIO\n" ); +return PTR_ERR (data->gpio_pin); + } + +if (of_property_read_bool (np,"assert-falling-edge" )) + data->assert_falling_edge = true; +return 0 ; +} +static unsigned long get_irqf_trigger_flags (const struct pps_gpio_device_data *data) {
@@ -90,53 +109,30 @@ get_irqf_trigger_flags(const struct pps_gpio_device_data *data)static int pps_gpio_probe (struct platform_device *pdev) {struct pps_gpio_device_data *data; -const char *gpio_label;int ret;int pps_default_params;const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; -struct device_node *np = pdev->dev.of_node;/* allocate space for device info */ - data =devm_kzalloc (&pdev->dev,sizeof (struct pps_gpio_device_data ), -GFP_KERNEL ); + data =devm_kzalloc (&pdev->dev,sizeof (*data),GFP_KERNEL );if (!data)return -ENOMEM ; +platform_set_drvdata (pdev, data); +/* GPIO setup */ if (pdata) { data->gpio_pin = pdata->gpio_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 ); -if (ret <0 ) { -dev_err (&pdev->dev,"failed to get GPIO from device tree\n" ); -return ret; - } - data->gpio_pin = ret; - gpio_label =PPS_GPIO_NAME ; - -if (of_get_property (np,"assert-falling-edge" ,NULL )) - data->assert_falling_edge = true; - } - -/* GPIO setup */ - ret =devm_gpio_request (&pdev->dev, data->gpio_pin, gpio_label); -if (ret) { -dev_err (&pdev->dev,"failed to request GPIO %u\n" , - data->gpio_pin); -return ret; - } - - ret =gpio_direction_input (data->gpio_pin); -if (ret) { -dev_err (&pdev->dev,"failed to set pin direction\n" ); -return -EINVAL ; + ret =pps_gpio_setup (pdev); +if (ret) +return -EINVAL ; }/* IRQ setup */ - ret =gpio_to_irq (data->gpio_pin); + ret =gpiod_to_irq (data->gpio_pin);if (ret <0 ) {dev_err (&pdev->dev,"failed to map GPIO to IRQ: %d\n" , ret);return -EINVAL ;
@@ -173,7 +169,6 @@ static int pps_gpio_probe(struct platform_device *pdev)return -EINVAL ; } -platform_set_drvdata (pdev, data);dev_info (data->pps->dev,"Registered IRQ %d as PPS source\n" , data->irq);
@@ -209,4 +204,4 @@ MODULE_AUTHOR("Ricardo Martins <[email protected]>");MODULE_AUTHOR ("James Nuss <[email protected]>" );MODULE_DESCRIPTION ("Use GPIO pin as PPS source" );MODULE_LICENSE ("GPL" ); -MODULE_VERSION ("1.0.0" ); +MODULE_VERSION ("1.1.0" );
include/linux/pps-gpio.h +1 -2
@@ -23,10 +23,9 @@#define _PPS_GPIO_Hstruct pps_gpio_platform_data { +struct gpio_desc *gpio_pin;bool assert_falling_edge;bool capture_clear; -unsigned int gpio_pin; -const char *gpio_label; };#endif /* _PPS_GPIO_H */
Signature · 1 line
2.12.3
PATCH v14 2/3 dt-bindings: pps: pps-gpio PPS ECHO implementation
This patch implements the device tree binding changes required for the pps echo functionality for pps-gpio, that sysfs claims is available already.
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.
--- Documentation/devicetree/bindings/pps/pps-gpio.txt | 7 +++++++ 1 file changed, 7 insertions(+)
Documentation/devicetree/bindings/pps/pps-gpio.txt +7 -0
@@ -7,6 +7,10 @@ Required properties: - compatible: should be "pps-gpio" - gpios: one PPS GPIO in the format described by ../gpio/gpio.txt +Additional required properties for the PPS ECHO functionality: +- echo-gpios: one PPS ECHO GPIO in the format described by ../gpio/gpio.txt +- echo-active-ms: duration in ms of the active portion of the echo pulse + Optional properties: - assert-falling-edge: when present, assert is indicated by a falling edge (instead of by a rising edge)
@@ -19,5 +23,8 @@ Example: gpios = <&gpio1 26 GPIO_ACTIVE_HIGH>; assert-falling-edge; + echo-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>; + echo-active-ms = <100>; + compatible = "pps-gpio"; };
Signature · 1 line
2.12.3
PATCH v14 3/3 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.
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.
--- drivers/pps/clients/pps-gpio.c | 89 ++++++++++++++++++++++++++++++++++++++++-- include/linux/pps-gpio.h | 2 + 2 files changed, 88 insertions(+), 3 deletions(-)
drivers/pps/clients/pps-gpio.c +86 -3
@@ -35,6 +35,8 @@#include <linux/list.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/timer.h> +#include <linux/jiffies.h> /* Info for each registered platform device */ struct pps_gpio_device_data {
@@ -42,8 +44,12 @@ struct pps_gpio_device_data {struct pps_device *pps;/* PPS source device */ struct pps_source_info info;/* PPS source information */ struct gpio_desc *gpio_pin;/* GPIO port descriptors */ +struct gpio_desc *echo_pin; +struct timer_list echo_timer;/* timer to reset echo active state */ bool assert_falling_edge;bool capture_clear; +unsigned int echo_active_ms;/* PPS echo active duration */ +unsigned long echo_timeout;/* timer timeout value in jiffies */ };/*
@@ -64,19 +70,57 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) rising_edge =gpiod_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);return IRQ_HANDLED ; } +static void pps_gpio_echo (struct pps_device *pps,int event,void *data) +{ +/* add_timer() needs to write into info->echo_timer */ +struct pps_gpio_device_data *info; + + info = data; + +switch (event) { +case PPS_CAPTUREASSERT : +if (pps->params.mode &PPS_ECHOASSERT ) +gpiod_set_value (info->echo_pin,1 ); +break ; + +case PPS_CAPTURECLEAR : +if (pps->params.mode &PPS_ECHOCLEAR ) +gpiod_set_value (info->echo_pin,1 ); +break ; + } + +/* fire the timer */ +if (info->pps->params.mode & (PPS_ECHOASSERT |PPS_ECHOCLEAR )) { + info->echo_timer.expires = jiffies + info->echo_timeout; +add_timer (&info->echo_timer); + } +} + +/* Timer callback to reset the echo pin to the inactive state */ +static void pps_gpio_echo_timer_callback (struct timer_list *t) +{ +const struct pps_gpio_device_data *info; + + info =from_timer (info, t, echo_timer); + +gpiod_set_value (info->echo_pin,0 ); +} +static int pps_gpio_setup (struct platform_device *pdev) {struct pps_gpio_device_data *data =platform_get_drvdata (pdev);struct device_node *np = pdev->dev.of_node; +int ret; +u32 value; data->gpio_pin = devm_gpiod_get(&pdev->dev, NULL, /* request "gpios" */
@@ -87,6 +131,33 @@ static int pps_gpio_setup(struct platform_device *pdev)return PTR_ERR (data->gpio_pin); } + data->echo_pin =devm_gpiod_get_optional (&pdev->dev, +"echo" , +GPIOD_OUT_LOW ); +if (data->echo_pin) { +if (IS_ERR (data->echo_pin)) { +dev_err (&pdev->dev,"failed to request ECHO GPIO\n" ); +return PTR_ERR (data->echo_pin); + } + + ret =of_property_read_u32 (np, +"echo-active-ms" , + &value); +if (ret) { +dev_err (&pdev->dev, +"failed to get echo-active-ms from OF\n" ); +return ret; + } + data->echo_active_ms = value; +/* sanity check on echo_active_ms */ +if (!data->echo_active_ms || data->echo_active_ms >999 ) { +dev_err (&pdev->dev, +"echo-active-ms: %u - bad value from OF\n" , + data->echo_active_ms); +return -EINVAL ; + } + } +if (of_property_read_bool (np,"assert-falling-edge" )) data->assert_falling_edge = true;return 0 ;
@@ -122,9 +193,11 @@ static int pps_gpio_probe(struct platform_device *pdev)/* GPIO setup */ if (pdata) { data->gpio_pin = pdata->gpio_pin; + data->echo_pin = pdata->echo_pin; data->assert_falling_edge = pdata->assert_falling_edge; data->capture_clear = pdata->capture_clear; + data->echo_active_ms = pdata->echo_active_ms; }else { ret =pps_gpio_setup (pdev);if (ret)
@@ -148,6 +221,11 @@ 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); +if (data->echo_pin) { + data->info.echo = pps_gpio_echo; + data->echo_timeout =msecs_to_jiffies (data->echo_active_ms); +timer_setup (&data->echo_timer, pps_gpio_echo_timer_callback,0 ); + }/* register PPS source */ pps_default_params =PPS_CAPTUREASSERT |PPS_OFFSETASSERT ;
@@ -180,6 +258,11 @@ static int pps_gpio_remove(struct platform_device *pdev)struct pps_gpio_device_data *data =platform_get_drvdata (pdev);pps_unregister_source (data->pps); +if (data->echo_pin) { +del_timer_sync (&data->echo_timer); +/* reset echo pin in any case */ +gpiod_set_value (data->echo_pin,0 ); + }dev_info (&pdev->dev,"removed IRQ %d as PPS source\n" , data->irq);return 0 ; }
@@ -204,4 +287,4 @@ MODULE_AUTHOR("Ricardo Martins <[email protected]>");MODULE_AUTHOR ("James Nuss <[email protected]>" );MODULE_DESCRIPTION ("Use GPIO pin as PPS source" );MODULE_LICENSE ("GPL" ); -MODULE_VERSION ("1.1.0" ); +MODULE_VERSION ("1.2.0" );
include/linux/pps-gpio.h +2 -0
@@ -24,8 +24,10 @@struct pps_gpio_platform_data {struct gpio_desc *gpio_pin; +struct gpio_desc *echo_pin;bool assert_falling_edge;bool capture_clear; +unsigned int echo_active_ms; };#endif /* _PPS_GPIO_H */
Signature · 1 line
2.12.3
Rodolfo Giometti
maintainerRe: [PATCH v14 3/3] pps: pps-gpio pps-echo implementation
Quoted 8 lines · Tom Burkart
This patch implements the pps echo functionality for pps-gpio, that sysfs claims is available already.
Configuration is done via device tree bindings.
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.
[snip]
drivers/pps/clients/pps-gpio.c static irqreturn_t pps_gpio_irq_handler(int irq, void *data) · line 87
Quoted 6 lines
drivers/pps/clients/pps-gpio.c +6
-0+static void pps_gpio_echo (struct pps_device *pps,int event,void *data) +{ +/* add_timer() needs to write into info->echo_timer */ +struct pps_gpio_device_data *info; + + info = data;
Maybe you can write as below and saving two lines and having better readability:
struct pps_gpio_device_data *info = data;
Quoted 8 lines
drivers/pps/clients/pps-gpio.c +8
-0+switch (event) { +case PPS_CAPTUREASSERT : +if (pps->params.mode &PPS_ECHOASSERT ) +gpiod_set_value (info->echo_pin,1 ); +break ; + +case PPS_CAPTURECLEAR : +if (pps->params.mode &PPS_ECHOCLEAR )
drivers/pps/clients/pps-gpio.c static irqreturn_t pps_gpio_irq_handler(int irq, void *data) · line 106
Quoted 10 lines
drivers/pps/clients/pps-gpio.c +10
-0+gpiod_set_value (info->echo_pin,1 ); +break ; + } + +/* fire the timer */ +if (info->pps->params.mode & (PPS_ECHOASSERT |PPS_ECHOCLEAR )) { + info->echo_timer.expires = jiffies + info->echo_timeout; +add_timer (&info->echo_timer); + } +}
I think is better firing the timer if and only if we set the echo GPIO otherwise it's useless...
Ciao,
Rodolfo
Signature · 4 lines
GNU/Linux Solutions e-mail: [email protected] Linux Device Driver [email protected] Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti
Rodolfo Giometti
maintainerRe: [PATCH v14 2/3] dt-bindings: pps: pps-gpio PPS ECHO implementation
In reply to Tom BurkartQuoted 7 lines · Tom Burkart
This patch implements the device tree binding changes required for the pps echo functionality for pps-gpio, that sysfs claims is available already.
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.
I think this patch it's OK but I'm asking to myself if it should be merged with next one... logically it describes what patch 3/3 does so why do we keep them separated?
Ciao,
Rodolfo
Signature · 4 lines
GNU/Linux Solutions e-mail: [email protected] Linux Device Driver [email protected] Embedded Systems phone: +39 349 2432127 UNIX programming skype: rodolfo.giometti