174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 216152045SJames Nuss /* 316152045SJames Nuss * pps-gpio.c -- PPS client driver using GPIO 416152045SJames Nuss * 516152045SJames Nuss * Copyright (C) 2010 Ricardo Martins <rasm@fe.up.pt> 616152045SJames Nuss * Copyright (C) 2011 James Nuss <jamesnuss@nanometrics.ca> 716152045SJames Nuss */ 816152045SJames Nuss 916152045SJames Nuss #define PPS_GPIO_NAME "pps-gpio" 1016152045SJames Nuss #define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt 1116152045SJames Nuss 1216152045SJames Nuss #include <linux/init.h> 1316152045SJames Nuss #include <linux/kernel.h> 1416152045SJames Nuss #include <linux/interrupt.h> 1516152045SJames Nuss #include <linux/module.h> 1616152045SJames Nuss #include <linux/platform_device.h> 1716152045SJames Nuss #include <linux/slab.h> 1816152045SJames Nuss #include <linux/pps_kernel.h> 1916152045SJames Nuss #include <linux/pps-gpio.h> 204461d651STom Burkart #include <linux/gpio/consumer.h> 2116152045SJames Nuss #include <linux/list.h> 22c5dbcf8bSJan Luebbe #include <linux/of_device.h> 23c5dbcf8bSJan Luebbe #include <linux/of_gpio.h> 244c69add4STom Burkart #include <linux/timer.h> 254c69add4STom Burkart #include <linux/jiffies.h> 2616152045SJames Nuss 2716152045SJames Nuss /* Info for each registered platform device */ 2816152045SJames Nuss struct pps_gpio_device_data { 2916152045SJames Nuss int irq; /* IRQ used as PPS source */ 3016152045SJames Nuss struct pps_device *pps; /* PPS source device */ 3116152045SJames Nuss struct pps_source_info info; /* PPS source information */ 324461d651STom Burkart struct gpio_desc *gpio_pin; /* GPIO port descriptors */ 334c69add4STom Burkart struct gpio_desc *echo_pin; 344c69add4STom Burkart struct timer_list echo_timer; /* timer to reset echo active state */ 35c5dbcf8bSJan Luebbe bool assert_falling_edge; 36c5dbcf8bSJan Luebbe bool capture_clear; 374c69add4STom Burkart unsigned int echo_active_ms; /* PPS echo active duration */ 384c69add4STom Burkart unsigned long echo_timeout; /* timer timeout value in jiffies */ 3916152045SJames Nuss }; 4016152045SJames Nuss 4116152045SJames Nuss /* 4216152045SJames Nuss * Report the PPS event 4316152045SJames Nuss */ 4416152045SJames Nuss 4516152045SJames Nuss static irqreturn_t pps_gpio_irq_handler(int irq, void *data) 4616152045SJames Nuss { 4716152045SJames Nuss const struct pps_gpio_device_data *info; 4816152045SJames Nuss struct pps_event_time ts; 4916152045SJames Nuss int rising_edge; 5016152045SJames Nuss 5116152045SJames Nuss /* Get the time stamp first */ 5216152045SJames Nuss pps_get_ts(&ts); 5316152045SJames Nuss 5416152045SJames Nuss info = data; 5516152045SJames Nuss 564461d651STom Burkart rising_edge = gpiod_get_value(info->gpio_pin); 57c5dbcf8bSJan Luebbe if ((rising_edge && !info->assert_falling_edge) || 58c5dbcf8bSJan Luebbe (!rising_edge && info->assert_falling_edge)) 594c69add4STom Burkart pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data); 60c5dbcf8bSJan Luebbe else if (info->capture_clear && 61c5dbcf8bSJan Luebbe ((rising_edge && info->assert_falling_edge) || 62c5dbcf8bSJan Luebbe (!rising_edge && !info->assert_falling_edge))) 634c69add4STom Burkart pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data); 6416152045SJames Nuss 6516152045SJames Nuss return IRQ_HANDLED; 6616152045SJames Nuss } 6716152045SJames Nuss 684c69add4STom Burkart /* This function will only be called when an ECHO GPIO is defined */ 694c69add4STom Burkart static void pps_gpio_echo(struct pps_device *pps, int event, void *data) 704c69add4STom Burkart { 714c69add4STom Burkart /* add_timer() needs to write into info->echo_timer */ 724c69add4STom Burkart struct pps_gpio_device_data *info = data; 734c69add4STom Burkart 744c69add4STom Burkart switch (event) { 754c69add4STom Burkart case PPS_CAPTUREASSERT: 764c69add4STom Burkart if (pps->params.mode & PPS_ECHOASSERT) 774c69add4STom Burkart gpiod_set_value(info->echo_pin, 1); 784c69add4STom Burkart break; 794c69add4STom Burkart 804c69add4STom Burkart case PPS_CAPTURECLEAR: 814c69add4STom Burkart if (pps->params.mode & PPS_ECHOCLEAR) 824c69add4STom Burkart gpiod_set_value(info->echo_pin, 1); 834c69add4STom Burkart break; 844c69add4STom Burkart } 854c69add4STom Burkart 864c69add4STom Burkart /* fire the timer */ 874c69add4STom Burkart if (info->pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) { 884c69add4STom Burkart info->echo_timer.expires = jiffies + info->echo_timeout; 894c69add4STom Burkart add_timer(&info->echo_timer); 904c69add4STom Burkart } 914c69add4STom Burkart } 924c69add4STom Burkart 934c69add4STom Burkart /* Timer callback to reset the echo pin to the inactive state */ 944c69add4STom Burkart static void pps_gpio_echo_timer_callback(struct timer_list *t) 954c69add4STom Burkart { 964c69add4STom Burkart const struct pps_gpio_device_data *info; 974c69add4STom Burkart 984c69add4STom Burkart info = from_timer(info, t, echo_timer); 994c69add4STom Burkart 1004c69add4STom Burkart gpiod_set_value(info->echo_pin, 0); 1014c69add4STom Burkart } 1024c69add4STom Burkart 1034461d651STom Burkart static int pps_gpio_setup(struct platform_device *pdev) 1044461d651STom Burkart { 1054461d651STom Burkart struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 1064461d651STom Burkart struct device_node *np = pdev->dev.of_node; 1074c69add4STom Burkart int ret; 1084c69add4STom Burkart u32 value; 1094461d651STom Burkart 1104461d651STom Burkart data->gpio_pin = devm_gpiod_get(&pdev->dev, 1114461d651STom Burkart NULL, /* request "gpios" */ 1124461d651STom Burkart GPIOD_IN); 1134461d651STom Burkart if (IS_ERR(data->gpio_pin)) { 1144461d651STom Burkart dev_err(&pdev->dev, 1154461d651STom Burkart "failed to request PPS GPIO\n"); 1164461d651STom Burkart return PTR_ERR(data->gpio_pin); 1174461d651STom Burkart } 1184461d651STom Burkart 1194c69add4STom Burkart data->echo_pin = devm_gpiod_get_optional(&pdev->dev, 1204c69add4STom Burkart "echo", 1214c69add4STom Burkart GPIOD_OUT_LOW); 1224c69add4STom Burkart if (IS_ERR(data->echo_pin)) { 1234c69add4STom Burkart dev_err(&pdev->dev, "failed to request ECHO GPIO\n"); 1244c69add4STom Burkart return PTR_ERR(data->echo_pin); 1254c69add4STom Burkart } 1264c69add4STom Burkart 127*a4780db3SAndy Shevchenko if (data->echo_pin) { 1284c69add4STom Burkart ret = of_property_read_u32(np, 1294c69add4STom Burkart "echo-active-ms", 1304c69add4STom Burkart &value); 1314c69add4STom Burkart if (ret) { 1324c69add4STom Burkart dev_err(&pdev->dev, 1334c69add4STom Burkart "failed to get echo-active-ms from OF\n"); 1344c69add4STom Burkart return ret; 1354c69add4STom Burkart } 1364c69add4STom Burkart data->echo_active_ms = value; 1374c69add4STom Burkart /* sanity check on echo_active_ms */ 1384c69add4STom Burkart if (!data->echo_active_ms || data->echo_active_ms > 999) { 1394c69add4STom Burkart dev_err(&pdev->dev, 1404c69add4STom Burkart "echo-active-ms: %u - bad value from OF\n", 1414c69add4STom Burkart data->echo_active_ms); 1424c69add4STom Burkart return -EINVAL; 1434c69add4STom Burkart } 1444c69add4STom Burkart } 1454c69add4STom Burkart 1464461d651STom Burkart if (of_property_read_bool(np, "assert-falling-edge")) 1474461d651STom Burkart data->assert_falling_edge = true; 1484461d651STom Burkart return 0; 1494461d651STom Burkart } 1504461d651STom Burkart 15116152045SJames Nuss static unsigned long 152c5dbcf8bSJan Luebbe get_irqf_trigger_flags(const struct pps_gpio_device_data *data) 15316152045SJames Nuss { 154c5dbcf8bSJan Luebbe unsigned long flags = data->assert_falling_edge ? 15516152045SJames Nuss IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; 15616152045SJames Nuss 157c5dbcf8bSJan Luebbe if (data->capture_clear) { 15816152045SJames Nuss flags |= ((flags & IRQF_TRIGGER_RISING) ? 15916152045SJames Nuss IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING); 16016152045SJames Nuss } 16116152045SJames Nuss 16216152045SJames Nuss return flags; 16316152045SJames Nuss } 16416152045SJames Nuss 16516152045SJames Nuss static int pps_gpio_probe(struct platform_device *pdev) 16616152045SJames Nuss { 16716152045SJames Nuss struct pps_gpio_device_data *data; 16816152045SJames Nuss int ret; 16916152045SJames Nuss int pps_default_params; 17016152045SJames Nuss const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; 17116152045SJames Nuss 17216152045SJames Nuss /* allocate space for device info */ 1734461d651STom Burkart data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 174c5dbcf8bSJan Luebbe if (!data) 1752a651822SJan Luebbe return -ENOMEM; 1764461d651STom Burkart platform_set_drvdata(pdev, data); 17716152045SJames Nuss 1784461d651STom Burkart /* GPIO setup */ 179c5dbcf8bSJan Luebbe if (pdata) { 180c5dbcf8bSJan Luebbe data->gpio_pin = pdata->gpio_pin; 1814c69add4STom Burkart data->echo_pin = pdata->echo_pin; 182c5dbcf8bSJan Luebbe 183c5dbcf8bSJan Luebbe data->assert_falling_edge = pdata->assert_falling_edge; 184c5dbcf8bSJan Luebbe data->capture_clear = pdata->capture_clear; 1854c69add4STom Burkart data->echo_active_ms = pdata->echo_active_ms; 186c5dbcf8bSJan Luebbe } else { 1874461d651STom Burkart ret = pps_gpio_setup(pdev); 1884461d651STom Burkart if (ret) 189c5dbcf8bSJan Luebbe return -EINVAL; 190c5dbcf8bSJan Luebbe } 191c5dbcf8bSJan Luebbe 192c5dbcf8bSJan Luebbe /* IRQ setup */ 1934461d651STom Burkart ret = gpiod_to_irq(data->gpio_pin); 194c5dbcf8bSJan Luebbe if (ret < 0) { 195c5dbcf8bSJan Luebbe dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret); 196c5dbcf8bSJan Luebbe return -EINVAL; 197c5dbcf8bSJan Luebbe } 198c5dbcf8bSJan Luebbe data->irq = ret; 199c5dbcf8bSJan Luebbe 20016152045SJames Nuss /* initialize PPS specific parts of the bookkeeping data structure. */ 20116152045SJames Nuss data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | 20216152045SJames Nuss PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC; 203c5dbcf8bSJan Luebbe if (data->capture_clear) 20416152045SJames Nuss data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR | 20516152045SJames Nuss PPS_ECHOCLEAR; 20616152045SJames Nuss data->info.owner = THIS_MODULE; 20716152045SJames Nuss snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 20816152045SJames Nuss pdev->name, pdev->id); 2094c69add4STom Burkart if (data->echo_pin) { 2104c69add4STom Burkart data->info.echo = pps_gpio_echo; 2114c69add4STom Burkart data->echo_timeout = msecs_to_jiffies(data->echo_active_ms); 2124c69add4STom Burkart timer_setup(&data->echo_timer, pps_gpio_echo_timer_callback, 0); 2134c69add4STom Burkart } 21416152045SJames Nuss 21516152045SJames Nuss /* register PPS source */ 21616152045SJames Nuss pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 217c5dbcf8bSJan Luebbe if (data->capture_clear) 21816152045SJames Nuss pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; 21916152045SJames Nuss data->pps = pps_register_source(&data->info, pps_default_params); 2203b1ad360SYueHaibing if (IS_ERR(data->pps)) { 221c5dbcf8bSJan Luebbe dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n", 222c5dbcf8bSJan Luebbe data->irq); 2233b1ad360SYueHaibing return PTR_ERR(data->pps); 22416152045SJames Nuss } 22516152045SJames Nuss 22616152045SJames Nuss /* register IRQ interrupt handler */ 227c5dbcf8bSJan Luebbe ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler, 228c5dbcf8bSJan Luebbe get_irqf_trigger_flags(data), data->info.name, data); 22916152045SJames Nuss if (ret) { 23016152045SJames Nuss pps_unregister_source(data->pps); 231c5dbcf8bSJan Luebbe dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq); 2322a651822SJan Luebbe return -EINVAL; 23316152045SJames Nuss } 23416152045SJames Nuss 235c5dbcf8bSJan Luebbe dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", 236c5dbcf8bSJan Luebbe data->irq); 23716152045SJames Nuss 23816152045SJames Nuss return 0; 23916152045SJames Nuss } 24016152045SJames Nuss 24116152045SJames Nuss static int pps_gpio_remove(struct platform_device *pdev) 24216152045SJames Nuss { 24316152045SJames Nuss struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 24416152045SJames Nuss 24516152045SJames Nuss pps_unregister_source(data->pps); 2464c69add4STom Burkart if (data->echo_pin) { 2474c69add4STom Burkart del_timer_sync(&data->echo_timer); 2484c69add4STom Burkart /* reset echo pin in any case */ 2494c69add4STom Burkart gpiod_set_value(data->echo_pin, 0); 2504c69add4STom Burkart } 251c5dbcf8bSJan Luebbe dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq); 25216152045SJames Nuss return 0; 25316152045SJames Nuss } 25416152045SJames Nuss 255c5dbcf8bSJan Luebbe static const struct of_device_id pps_gpio_dt_ids[] = { 256c5dbcf8bSJan Luebbe { .compatible = "pps-gpio", }, 257c5dbcf8bSJan Luebbe { /* sentinel */ } 258c5dbcf8bSJan Luebbe }; 259c5dbcf8bSJan Luebbe MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids); 260c5dbcf8bSJan Luebbe 26116152045SJames Nuss static struct platform_driver pps_gpio_driver = { 26216152045SJames Nuss .probe = pps_gpio_probe, 2630fe763c5SGreg Kroah-Hartman .remove = pps_gpio_remove, 26416152045SJames Nuss .driver = { 26516152045SJames Nuss .name = PPS_GPIO_NAME, 2661a10bd94SSachin Kamat .of_match_table = pps_gpio_dt_ids, 26716152045SJames Nuss }, 26816152045SJames Nuss }; 26916152045SJames Nuss 27005212be3SJan Luebbe module_platform_driver(pps_gpio_driver); 27116152045SJames Nuss MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>"); 27216152045SJames Nuss MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 27316152045SJames Nuss MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 27416152045SJames Nuss MODULE_LICENSE("GPL"); 2754c69add4STom Burkart MODULE_VERSION("1.2.0"); 276