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 = kzalloc(sizeof(struct pps_gpio_device_data), GFP_KERNEL); 132 if (data == NULL) { 133 err = -ENOMEM; 134 goto return_error; 135 } 136 137 /* initialize PPS specific parts of the bookkeeping data structure. */ 138 data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | 139 PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC; 140 if (pdata->capture_clear) 141 data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR | 142 PPS_ECHOCLEAR; 143 data->info.owner = THIS_MODULE; 144 snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d", 145 pdev->name, pdev->id); 146 147 /* register PPS source */ 148 pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 149 if (pdata->capture_clear) 150 pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; 151 data->pps = pps_register_source(&data->info, pps_default_params); 152 if (data->pps == NULL) { 153 kfree(data); 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 kfree(data); 168 pr_err("failed to acquire IRQ %d\n", irq); 169 err = -EINVAL; 170 goto return_error; 171 } 172 173 platform_set_drvdata(pdev, data); 174 dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq); 175 176 return 0; 177 178 return_error: 179 gpio_free(pdata->gpio_pin); 180 return err; 181 } 182 183 static int pps_gpio_remove(struct platform_device *pdev) 184 { 185 struct pps_gpio_device_data *data = platform_get_drvdata(pdev); 186 const struct pps_gpio_platform_data *pdata = data->pdata; 187 188 platform_set_drvdata(pdev, NULL); 189 free_irq(data->irq, data); 190 gpio_free(pdata->gpio_pin); 191 pps_unregister_source(data->pps); 192 pr_info("removed IRQ %d as PPS source\n", data->irq); 193 kfree(data); 194 return 0; 195 } 196 197 static struct platform_driver pps_gpio_driver = { 198 .probe = pps_gpio_probe, 199 .remove = __devexit_p(pps_gpio_remove), 200 .driver = { 201 .name = PPS_GPIO_NAME, 202 .owner = THIS_MODULE 203 }, 204 }; 205 206 static int __init pps_gpio_init(void) 207 { 208 int ret = platform_driver_register(&pps_gpio_driver); 209 if (ret < 0) 210 pr_err("failed to register platform driver\n"); 211 return ret; 212 } 213 214 static void __exit pps_gpio_exit(void) 215 { 216 platform_driver_unregister(&pps_gpio_driver); 217 pr_debug("unregistered platform driver\n"); 218 } 219 220 module_init(pps_gpio_init); 221 module_exit(pps_gpio_exit); 222 223 MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>"); 224 MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 225 MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 226 MODULE_LICENSE("GPL"); 227 MODULE_VERSION("1.0.0"); 228