116152045SJames Nuss /* 216152045SJames Nuss * pps-gpio.c -- PPS client driver using GPIO 316152045SJames Nuss * 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 * This program is free software; you can redistribute it and/or modify 916152045SJames Nuss * it under the terms of the GNU General Public License as published by 1016152045SJames Nuss * the Free Software Foundation; either version 2 of the License, or 1116152045SJames Nuss * (at your option) any later version. 1216152045SJames Nuss * 1316152045SJames Nuss * This program is distributed in the hope that it will be useful, 1416152045SJames Nuss * but WITHOUT ANY WARRANTY; without even the implied warranty of 1516152045SJames Nuss * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1616152045SJames Nuss * GNU General Public License for more details. 1716152045SJames Nuss * 1816152045SJames Nuss * You should have received a copy of the GNU General Public License 1916152045SJames Nuss * along with this program; if not, write to the Free Software 2016152045SJames Nuss * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2116152045SJames Nuss */ 2216152045SJames Nuss 2316152045SJames Nuss #define PPS_GPIO_NAME "pps-gpio" 2416152045SJames Nuss #define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt 2516152045SJames Nuss 2616152045SJames Nuss #include <linux/init.h> 2716152045SJames Nuss #include <linux/kernel.h> 2816152045SJames Nuss #include <linux/interrupt.h> 2916152045SJames Nuss #include <linux/module.h> 3016152045SJames Nuss #include <linux/platform_device.h> 3116152045SJames Nuss #include <linux/slab.h> 3216152045SJames Nuss #include <linux/pps_kernel.h> 3316152045SJames Nuss #include <linux/pps-gpio.h> 3416152045SJames Nuss #include <linux/gpio.h> 3516152045SJames Nuss #include <linux/list.h> 3616152045SJames Nuss 3716152045SJames Nuss /* Info for each registered platform device */ 3816152045SJames Nuss struct pps_gpio_device_data { 3916152045SJames Nuss int irq; /* IRQ used as PPS source */ 4016152045SJames Nuss struct pps_device *pps; /* PPS source device */ 4116152045SJames Nuss struct pps_source_info info; /* PPS source information */ 4216152045SJames Nuss const struct pps_gpio_platform_data *pdata; 4316152045SJames Nuss }; 4416152045SJames Nuss 4516152045SJames Nuss /* 4616152045SJames Nuss * Report the PPS event 4716152045SJames Nuss */ 4816152045SJames Nuss 4916152045SJames Nuss static irqreturn_t pps_gpio_irq_handler(int irq, void *data) 5016152045SJames Nuss { 5116152045SJames Nuss const struct pps_gpio_device_data *info; 5216152045SJames Nuss struct pps_event_time ts; 5316152045SJames Nuss int rising_edge; 5416152045SJames Nuss 5516152045SJames Nuss /* Get the time stamp first */ 5616152045SJames Nuss pps_get_ts(&ts); 5716152045SJames Nuss 5816152045SJames Nuss info = data; 5916152045SJames Nuss 6016152045SJames Nuss rising_edge = gpio_get_value(info->pdata->gpio_pin); 6116152045SJames Nuss if ((rising_edge && !info->pdata->assert_falling_edge) || 6216152045SJames Nuss (!rising_edge && info->pdata->assert_falling_edge)) 6316152045SJames Nuss pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL); 6416152045SJames Nuss else if (info->pdata->capture_clear && 6516152045SJames Nuss ((rising_edge && info->pdata->assert_falling_edge) || 6616152045SJames Nuss (!rising_edge && !info->pdata->assert_falling_edge))) 6716152045SJames Nuss pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL); 6816152045SJames Nuss 6916152045SJames Nuss return IRQ_HANDLED; 7016152045SJames Nuss } 7116152045SJames Nuss 7216152045SJames Nuss static int pps_gpio_setup(struct platform_device *pdev) 7316152045SJames Nuss { 7416152045SJames Nuss int ret; 7516152045SJames Nuss const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; 7616152045SJames Nuss 7716152045SJames Nuss ret = gpio_request(pdata->gpio_pin, pdata->gpio_label); 7816152045SJames Nuss if (ret) { 7916152045SJames Nuss pr_warning("failed to request GPIO %u\n", pdata->gpio_pin); 8016152045SJames Nuss return -EINVAL; 8116152045SJames Nuss } 8216152045SJames Nuss 8316152045SJames Nuss ret = gpio_direction_input(pdata->gpio_pin); 8416152045SJames Nuss if (ret) { 8516152045SJames Nuss pr_warning("failed to set pin direction\n"); 8616152045SJames Nuss gpio_free(pdata->gpio_pin); 8716152045SJames Nuss return -EINVAL; 8816152045SJames Nuss } 8916152045SJames Nuss 9016152045SJames Nuss return 0; 9116152045SJames Nuss } 9216152045SJames Nuss 9316152045SJames Nuss static unsigned long 9416152045SJames Nuss get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata) 9516152045SJames Nuss { 9616152045SJames Nuss unsigned long flags = pdata->assert_falling_edge ? 9716152045SJames Nuss IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; 9816152045SJames Nuss 9916152045SJames Nuss if (pdata->capture_clear) { 10016152045SJames Nuss flags |= ((flags & IRQF_TRIGGER_RISING) ? 10116152045SJames Nuss IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING); 10216152045SJames Nuss } 10316152045SJames Nuss 10416152045SJames Nuss return flags; 10516152045SJames Nuss } 10616152045SJames Nuss 10716152045SJames Nuss static int pps_gpio_probe(struct platform_device *pdev) 10816152045SJames Nuss { 10916152045SJames Nuss struct pps_gpio_device_data *data; 11016152045SJames Nuss int irq; 11116152045SJames Nuss int ret; 11216152045SJames Nuss int err; 11316152045SJames Nuss int pps_default_params; 11416152045SJames Nuss const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; 11516152045SJames Nuss 11616152045SJames Nuss 11716152045SJames Nuss /* GPIO setup */ 11816152045SJames Nuss ret = pps_gpio_setup(pdev); 11916152045SJames Nuss if (ret) 12016152045SJames Nuss return -EINVAL; 12116152045SJames Nuss 12216152045SJames Nuss /* IRQ setup */ 12316152045SJames Nuss irq = gpio_to_irq(pdata->gpio_pin); 12416152045SJames Nuss if (irq < 0) { 12516152045SJames Nuss pr_err("failed to map GPIO to IRQ: %d\n", irq); 12616152045SJames Nuss err = -EINVAL; 12716152045SJames Nuss goto return_error; 12816152045SJames Nuss } 12916152045SJames Nuss 13016152045SJames Nuss /* allocate space for device info */ 131507063b2SJulia Lawall data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data), 132507063b2SJulia Lawall GFP_KERNEL); 13316152045SJames Nuss if (data == NULL) { 13416152045SJames Nuss err = -ENOMEM; 13516152045SJames Nuss goto return_error; 13616152045SJames Nuss } 13716152045SJames Nuss 13816152045SJames Nuss /* initialize PPS specific parts of the bookkeeping data structure. */ 13916152045SJames Nuss data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | 14016152045SJames Nuss PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC; 14116152045SJames Nuss if (pdata->capture_clear) 14216152045SJames Nuss data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR | 14316152045SJames Nuss PPS_ECHOCLEAR; 14416152045SJames Nuss data->info.owner = THIS_MODULE; 14516152045SJames Nuss snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 14616152045SJames Nuss pdev->name, pdev->id); 14716152045SJames Nuss 14816152045SJames Nuss /* register PPS source */ 14916152045SJames Nuss pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 15016152045SJames Nuss if (pdata->capture_clear) 15116152045SJames Nuss pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; 15216152045SJames Nuss data->pps = pps_register_source(&data->info, pps_default_params); 15316152045SJames Nuss if (data->pps == NULL) { 15416152045SJames Nuss pr_err("failed to register IRQ %d as PPS source\n", irq); 15516152045SJames Nuss err = -EINVAL; 15616152045SJames Nuss goto return_error; 15716152045SJames Nuss } 15816152045SJames Nuss 15916152045SJames Nuss data->irq = irq; 16016152045SJames Nuss data->pdata = pdata; 16116152045SJames Nuss 16216152045SJames Nuss /* register IRQ interrupt handler */ 16316152045SJames Nuss ret = request_irq(irq, pps_gpio_irq_handler, 16416152045SJames Nuss get_irqf_trigger_flags(pdata), data->info.name, data); 16516152045SJames Nuss if (ret) { 16616152045SJames Nuss pps_unregister_source(data->pps); 16716152045SJames Nuss pr_err("failed to acquire IRQ %d\n", irq); 16816152045SJames Nuss err = -EINVAL; 16916152045SJames Nuss goto return_error; 17016152045SJames Nuss } 17116152045SJames Nuss 17216152045SJames Nuss platform_set_drvdata(pdev, data); 17316152045SJames Nuss dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq); 17416152045SJames Nuss 17516152045SJames Nuss return 0; 17616152045SJames Nuss 17716152045SJames Nuss return_error: 17816152045SJames Nuss gpio_free(pdata->gpio_pin); 17916152045SJames Nuss return err; 18016152045SJames Nuss } 18116152045SJames Nuss 18216152045SJames Nuss static int pps_gpio_remove(struct platform_device *pdev) 18316152045SJames Nuss { 18416152045SJames Nuss struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 18516152045SJames Nuss const struct pps_gpio_platform_data *pdata = data->pdata; 18616152045SJames Nuss 18716152045SJames Nuss platform_set_drvdata(pdev, NULL); 18816152045SJames Nuss free_irq(data->irq, data); 18916152045SJames Nuss gpio_free(pdata->gpio_pin); 19016152045SJames Nuss pps_unregister_source(data->pps); 19116152045SJames Nuss pr_info("removed IRQ %d as PPS source\n", data->irq); 19216152045SJames Nuss return 0; 19316152045SJames Nuss } 19416152045SJames Nuss 19516152045SJames Nuss static struct platform_driver pps_gpio_driver = { 19616152045SJames Nuss .probe = pps_gpio_probe, 1970fe763c5SGreg Kroah-Hartman .remove = pps_gpio_remove, 19816152045SJames Nuss .driver = { 19916152045SJames Nuss .name = PPS_GPIO_NAME, 20016152045SJames Nuss .owner = THIS_MODULE 20116152045SJames Nuss }, 20216152045SJames Nuss }; 20316152045SJames Nuss 20416152045SJames Nuss static int __init pps_gpio_init(void) 20516152045SJames Nuss { 20616152045SJames Nuss int ret = platform_driver_register(&pps_gpio_driver); 20716152045SJames Nuss if (ret < 0) 20816152045SJames Nuss pr_err("failed to register platform driver\n"); 20916152045SJames Nuss return ret; 21016152045SJames Nuss } 21116152045SJames Nuss 21216152045SJames Nuss static void __exit pps_gpio_exit(void) 21316152045SJames Nuss { 21416152045SJames Nuss platform_driver_unregister(&pps_gpio_driver); 21516152045SJames Nuss pr_debug("unregistered platform driver\n"); 21616152045SJames Nuss } 21716152045SJames Nuss 21816152045SJames Nuss module_init(pps_gpio_init); 21916152045SJames Nuss module_exit(pps_gpio_exit); 22016152045SJames Nuss 22116152045SJames Nuss MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>"); 22216152045SJames Nuss MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 22316152045SJames Nuss MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 22416152045SJames Nuss MODULE_LICENSE("GPL"); 22516152045SJames Nuss MODULE_VERSION("1.0.0"); 226