xref: /openbmc/linux/drivers/pps/clients/pps-gpio.c (revision 0da85d1e)
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 #include <linux/of_device.h>
37 #include <linux/of_gpio.h>
38 
39 /* Info for each registered platform device */
40 struct pps_gpio_device_data {
41 	int irq;			/* IRQ used as PPS source */
42 	struct pps_device *pps;		/* PPS source device */
43 	struct pps_source_info info;	/* PPS source information */
44 	bool assert_falling_edge;
45 	bool capture_clear;
46 	unsigned int gpio_pin;
47 };
48 
49 /*
50  * Report the PPS event
51  */
52 
53 static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
54 {
55 	const struct pps_gpio_device_data *info;
56 	struct pps_event_time ts;
57 	int rising_edge;
58 
59 	/* Get the time stamp first */
60 	pps_get_ts(&ts);
61 
62 	info = data;
63 
64 	rising_edge = gpio_get_value(info->gpio_pin);
65 	if ((rising_edge && !info->assert_falling_edge) ||
66 			(!rising_edge && info->assert_falling_edge))
67 		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
68 	else if (info->capture_clear &&
69 			((rising_edge && info->assert_falling_edge) ||
70 			 (!rising_edge && !info->assert_falling_edge)))
71 		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
72 
73 	return IRQ_HANDLED;
74 }
75 
76 static unsigned long
77 get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
78 {
79 	unsigned long flags = data->assert_falling_edge ?
80 		IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
81 
82 	if (data->capture_clear) {
83 		flags |= ((flags & IRQF_TRIGGER_RISING) ?
84 				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
85 	}
86 
87 	return flags;
88 }
89 
90 static int pps_gpio_probe(struct platform_device *pdev)
91 {
92 	struct pps_gpio_device_data *data;
93 	const char *gpio_label;
94 	int ret;
95 	int pps_default_params;
96 	const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
97 	struct device_node *np = pdev->dev.of_node;
98 
99 	/* allocate space for device info */
100 	data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data),
101 			GFP_KERNEL);
102 	if (!data)
103 		return -ENOMEM;
104 
105 	if (pdata) {
106 		data->gpio_pin = pdata->gpio_pin;
107 		gpio_label = pdata->gpio_label;
108 
109 		data->assert_falling_edge = pdata->assert_falling_edge;
110 		data->capture_clear = pdata->capture_clear;
111 	} else {
112 		ret = of_get_gpio(np, 0);
113 		if (ret < 0) {
114 			dev_err(&pdev->dev, "failed to get GPIO from device tree\n");
115 			return ret;
116 		}
117 		data->gpio_pin = ret;
118 		gpio_label = PPS_GPIO_NAME;
119 
120 		if (of_get_property(np, "assert-falling-edge", NULL))
121 			data->assert_falling_edge = true;
122 	}
123 
124 	/* GPIO setup */
125 	ret = devm_gpio_request(&pdev->dev, data->gpio_pin, gpio_label);
126 	if (ret) {
127 		dev_err(&pdev->dev, "failed to request GPIO %u\n",
128 			data->gpio_pin);
129 		return ret;
130 	}
131 
132 	ret = gpio_direction_input(data->gpio_pin);
133 	if (ret) {
134 		dev_err(&pdev->dev, "failed to set pin direction\n");
135 		return -EINVAL;
136 	}
137 
138 	/* IRQ setup */
139 	ret = gpio_to_irq(data->gpio_pin);
140 	if (ret < 0) {
141 		dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret);
142 		return -EINVAL;
143 	}
144 	data->irq = ret;
145 
146 	/* initialize PPS specific parts of the bookkeeping data structure. */
147 	data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
148 		PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
149 	if (data->capture_clear)
150 		data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
151 			PPS_ECHOCLEAR;
152 	data->info.owner = THIS_MODULE;
153 	snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
154 		 pdev->name, pdev->id);
155 
156 	/* register PPS source */
157 	pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
158 	if (data->capture_clear)
159 		pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
160 	data->pps = pps_register_source(&data->info, pps_default_params);
161 	if (data->pps == NULL) {
162 		dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n",
163 			data->irq);
164 		return -EINVAL;
165 	}
166 
167 	/* register IRQ interrupt handler */
168 	ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler,
169 			get_irqf_trigger_flags(data), data->info.name, data);
170 	if (ret) {
171 		pps_unregister_source(data->pps);
172 		dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq);
173 		return -EINVAL;
174 	}
175 
176 	platform_set_drvdata(pdev, data);
177 	dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n",
178 		 data->irq);
179 
180 	return 0;
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 
187 	pps_unregister_source(data->pps);
188 	dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq);
189 	return 0;
190 }
191 
192 static const struct of_device_id pps_gpio_dt_ids[] = {
193 	{ .compatible = "pps-gpio", },
194 	{ /* sentinel */ }
195 };
196 MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids);
197 
198 static struct platform_driver pps_gpio_driver = {
199 	.probe		= pps_gpio_probe,
200 	.remove		= pps_gpio_remove,
201 	.driver		= {
202 		.name	= PPS_GPIO_NAME,
203 		.of_match_table	= pps_gpio_dt_ids,
204 	},
205 };
206 
207 module_platform_driver(pps_gpio_driver);
208 MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
209 MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
210 MODULE_DESCRIPTION("Use GPIO pin as PPS source");
211 MODULE_LICENSE("GPL");
212 MODULE_VERSION("1.0.0");
213