Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8-rc3 227 lines 6.1 kB view raw
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 */ 38struct 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 49static 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 72static 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 93static unsigned long 94get_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 107static 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 178return_error: 179 gpio_free(pdata->gpio_pin); 180 return err; 181} 182 183static 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 197static struct platform_driver pps_gpio_driver = { 198 .probe = pps_gpio_probe, 199 .remove = pps_gpio_remove, 200 .driver = { 201 .name = PPS_GPIO_NAME, 202 .owner = THIS_MODULE 203 }, 204}; 205 206static 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 214static void __exit pps_gpio_exit(void) 215{ 216 platform_driver_unregister(&pps_gpio_driver); 217 pr_debug("unregistered platform driver\n"); 218} 219 220module_init(pps_gpio_init); 221module_exit(pps_gpio_exit); 222 223MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>"); 224MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>"); 225MODULE_DESCRIPTION("Use GPIO pin as PPS source"); 226MODULE_LICENSE("GPL"); 227MODULE_VERSION("1.0.0");