1 /* 2 * pps-gpio.c -- PPS client driver using GPIO 3 * 4 * 5 * Copyright (C) 2010 Ricardo Martins <rasm@fe.up.pt> 6 * Copyright (C) 2011 James Nuss <jamesnuss@nanometrics.ca> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #define PPS_GPIO_NAME "pps-gpio" 24 #define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt 25 26 #include <linux/init.h> 27 #include <linux/kernel.h> 28 #include <linux/interrupt.h> 29 #include <linux/module.h> 30 #include <linux/platform_device.h> 31 #include <linux/slab.h> 32 #include <linux/pps_kernel.h> 33 #include <linux/pps-gpio.h> 34 #include <linux/gpio.h> 35 #include <linux/list.h> 36 37 /* Info for each registered platform device */ 38 struct pps_gpio_device_data { 39 int irq; /* IRQ used as PPS source */ 40 struct pps_device *pps; /* PPS source device */ 41 struct pps_source_info info; /* PPS source information */ 42 const struct pps_gpio_platform_data *pdata; 43 }; 44 45 /* 46 * Report the PPS event 47 */ 48 49 static irqreturn_t pps_gpio_irq_handler(int irq, void *data) 50 { 51 const struct pps_gpio_device_data *info; 52 struct pps_event_time ts; 53 int rising_edge; 54 55 /* Get the time stamp first */ 56 pps_get_ts(&ts); 57 58 info = data; 59 60 rising_edge = gpio_get_value(info->pdata->gpio_pin); 61 if ((rising_edge && !info->pdata->assert_falling_edge) || 62 (!rising_edge && info->pdata->assert_falling_edge)) 63 pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL); 64 else if (info->pdata->capture_clear && 65 ((rising_edge && info->pdata->assert_falling_edge) || 66 (!rising_edge && !info->pdata->assert_falling_edge))) 67 pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL); 68 69 return IRQ_HANDLED; 70 } 71 72 static int pps_gpio_setup(struct platform_device *pdev) 73 { 74 int ret; 75 const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; 76 77 ret = gpio_request(pdata->gpio_pin, pdata->gpio_label); 78 if (ret) { 79 pr_warning("failed to request GPIO %u\n", pdata->gpio_pin); 80 return -EINVAL; 81 } 82 83 ret = gpio_direction_input(pdata->gpio_pin); 84 if (ret) { 85 pr_warning("failed to set pin direction\n"); 86 gpio_free(pdata->gpio_pin); 87 return -EINVAL; 88 } 89 90 return 0; 91 } 92 93 static unsigned long 94 get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata) 95 { 96 unsigned long flags = pdata->assert_falling_edge ? 97 IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; 98 99 if (pdata->capture_clear) { 100 flags |= ((flags & IRQF_TRIGGER_RISING) ? 101 IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING); 102 } 103 104 return flags; 105 } 106 107 static int pps_gpio_probe(struct platform_device *pdev) 108 { 109 struct pps_gpio_device_data *data; 110 int irq; 111 int ret; 112 int err; 113 int pps_default_params; 114 const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; 115 116 117 /* GPIO setup */ 118 ret = pps_gpio_setup(pdev); 119 if (ret) 120 return -EINVAL; 121 122 /* IRQ setup */ 123 irq = gpio_to_irq(pdata->gpio_pin); 124 if (irq < 0) { 125 pr_err("failed to map GPIO to IRQ: %d\n", irq); 126 err = -EINVAL; 127 goto return_error; 128 } 129 130 /* allocate space for device info */ 131 data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data), 132 GFP_KERNEL); 133 if (data == NULL) { 134 err = -ENOMEM; 135 goto return_error; 136 } 137 138 /* initialize PPS specific parts of the bookkeeping data structure. */ 139 data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | 140 PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC; 141 if (pdata->capture_clear) 142 data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR | 143 PPS_ECHOCLEAR; 144 data->info.owner = THIS_MODULE; 145 snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 146 pdev->name, pdev->id); 147 148 /* register PPS source */ 149 pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 150 if (pdata->capture_clear) 151 pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; 152 data->pps = pps_register_source(&data->info, pps_default_params); 153 if (data->pps == NULL) { 154 pr_err("failed to register IRQ %d as PPS source\n", irq); 155 err = -EINVAL; 156 goto return_error; 157 } 158 159 data->irq = irq; 160 data->pdata = pdata; 161 162 /* register IRQ interrupt handler */ 163 ret = request_irq(irq, pps_gpio_irq_handler, 164 get_irqf_trigger_flags(pdata), data->info.name, data); 165 if (ret) { 166 pps_unregister_source(data->pps); 167 pr_err("failed to acquire IRQ %d\n", irq); 168 err = -EINVAL; 169 goto return_error; 170 } 171 172 platform_set_drvdata(pdev, data); 173 dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq); 174 175 return 0; 176 177 return_error: 178 gpio_free(pdata->gpio_pin); 179 return err; 180 } 181 182 static int pps_gpio_remove(struct platform_device *pdev) 183 { 184 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 185 const struct pps_gpio_platform_data *pdata = data->pdata; 186 187 platform_set_drvdata(pdev, NULL); 188 free_irq(data->irq, data); 189 gpio_free(pdata->gpio_pin); 190 pps_unregister_source(data->pps); 191 pr_info("removed IRQ %d as PPS source\n", data->irq); 192 return 0; 193 } 194 195 static struct platform_driver pps_gpio_driver = { 196 .probe = pps_gpio_probe, 197 .remove = pps_gpio_remove, 198 .driver = { 199 .name = PPS_GPIO_NAME, 200 .owner = THIS_MODULE 201 }, 202 }; 203 204 static int __init pps_gpio_init(void) 205 { 206 int ret = platform_driver_register(&pps_gpio_driver); 207 if (ret < 0) 208 pr_err("failed to register platform driver\n"); 209 return ret; 210 } 211 212 static void __exit pps_gpio_exit(void) 213 { 214 platform_driver_unregister(&pps_gpio_driver); 215 pr_debug("unregistered platform driver\n"); 216 } 217 218 module_init(pps_gpio_init); 219 module_exit(pps_gpio_exit); 220 221 MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>"); 222 MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 223 MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 224 MODULE_LICENSE("GPL"); 225 MODULE_VERSION("1.0.0"); 226