xref: /openbmc/linux/drivers/pps/clients/pps-gpio.c (revision 4461d651)
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>
344461d651STom Burkart #include <linux/gpio/consumer.h>
3516152045SJames Nuss #include <linux/list.h>
36c5dbcf8bSJan Luebbe #include <linux/of_device.h>
37c5dbcf8bSJan Luebbe #include <linux/of_gpio.h>
3816152045SJames Nuss 
3916152045SJames Nuss /* Info for each registered platform device */
4016152045SJames Nuss struct pps_gpio_device_data {
4116152045SJames Nuss 	int irq;			/* IRQ used as PPS source */
4216152045SJames Nuss 	struct pps_device *pps;		/* PPS source device */
4316152045SJames Nuss 	struct pps_source_info info;	/* PPS source information */
444461d651STom Burkart 	struct gpio_desc *gpio_pin;	/* GPIO port descriptors */
45c5dbcf8bSJan Luebbe 	bool assert_falling_edge;
46c5dbcf8bSJan Luebbe 	bool capture_clear;
4716152045SJames Nuss };
4816152045SJames Nuss 
4916152045SJames Nuss /*
5016152045SJames Nuss  * Report the PPS event
5116152045SJames Nuss  */
5216152045SJames Nuss 
5316152045SJames Nuss static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
5416152045SJames Nuss {
5516152045SJames Nuss 	const struct pps_gpio_device_data *info;
5616152045SJames Nuss 	struct pps_event_time ts;
5716152045SJames Nuss 	int rising_edge;
5816152045SJames Nuss 
5916152045SJames Nuss 	/* Get the time stamp first */
6016152045SJames Nuss 	pps_get_ts(&ts);
6116152045SJames Nuss 
6216152045SJames Nuss 	info = data;
6316152045SJames Nuss 
644461d651STom Burkart 	rising_edge = gpiod_get_value(info->gpio_pin);
65c5dbcf8bSJan Luebbe 	if ((rising_edge && !info->assert_falling_edge) ||
66c5dbcf8bSJan Luebbe 			(!rising_edge && info->assert_falling_edge))
6716152045SJames Nuss 		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
68c5dbcf8bSJan Luebbe 	else if (info->capture_clear &&
69c5dbcf8bSJan Luebbe 			((rising_edge && info->assert_falling_edge) ||
70c5dbcf8bSJan Luebbe 			(!rising_edge && !info->assert_falling_edge)))
7116152045SJames Nuss 		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
7216152045SJames Nuss 
7316152045SJames Nuss 	return IRQ_HANDLED;
7416152045SJames Nuss }
7516152045SJames Nuss 
764461d651STom Burkart static int pps_gpio_setup(struct platform_device *pdev)
774461d651STom Burkart {
784461d651STom Burkart 	struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
794461d651STom Burkart 	struct device_node *np = pdev->dev.of_node;
804461d651STom Burkart 
814461d651STom Burkart 	data->gpio_pin = devm_gpiod_get(&pdev->dev,
824461d651STom Burkart 		NULL,	/* request "gpios" */
834461d651STom Burkart 		GPIOD_IN);
844461d651STom Burkart 	if (IS_ERR(data->gpio_pin)) {
854461d651STom Burkart 		dev_err(&pdev->dev,
864461d651STom Burkart 			"failed to request PPS GPIO\n");
874461d651STom Burkart 		return PTR_ERR(data->gpio_pin);
884461d651STom Burkart 	}
894461d651STom Burkart 
904461d651STom Burkart 	if (of_property_read_bool(np, "assert-falling-edge"))
914461d651STom Burkart 		data->assert_falling_edge = true;
924461d651STom Burkart 	return 0;
934461d651STom Burkart }
944461d651STom Burkart 
9516152045SJames Nuss static unsigned long
96c5dbcf8bSJan Luebbe get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
9716152045SJames Nuss {
98c5dbcf8bSJan Luebbe 	unsigned long flags = data->assert_falling_edge ?
9916152045SJames Nuss 		IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
10016152045SJames Nuss 
101c5dbcf8bSJan Luebbe 	if (data->capture_clear) {
10216152045SJames Nuss 		flags |= ((flags & IRQF_TRIGGER_RISING) ?
10316152045SJames Nuss 				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
10416152045SJames Nuss 	}
10516152045SJames Nuss 
10616152045SJames Nuss 	return flags;
10716152045SJames Nuss }
10816152045SJames Nuss 
10916152045SJames Nuss static int pps_gpio_probe(struct platform_device *pdev)
11016152045SJames Nuss {
11116152045SJames Nuss 	struct pps_gpio_device_data *data;
11216152045SJames Nuss 	int ret;
11316152045SJames Nuss 	int pps_default_params;
11416152045SJames Nuss 	const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
11516152045SJames Nuss 
11616152045SJames Nuss 	/* allocate space for device info */
1174461d651STom Burkart 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
118c5dbcf8bSJan Luebbe 	if (!data)
1192a651822SJan Luebbe 		return -ENOMEM;
1204461d651STom Burkart 	platform_set_drvdata(pdev, data);
12116152045SJames Nuss 
1224461d651STom Burkart 	/* GPIO setup */
123c5dbcf8bSJan Luebbe 	if (pdata) {
124c5dbcf8bSJan Luebbe 		data->gpio_pin = pdata->gpio_pin;
125c5dbcf8bSJan Luebbe 
126c5dbcf8bSJan Luebbe 		data->assert_falling_edge = pdata->assert_falling_edge;
127c5dbcf8bSJan Luebbe 		data->capture_clear = pdata->capture_clear;
128c5dbcf8bSJan Luebbe 	} else {
1294461d651STom Burkart 		ret = pps_gpio_setup(pdev);
1304461d651STom Burkart 		if (ret)
131c5dbcf8bSJan Luebbe 			return -EINVAL;
132c5dbcf8bSJan Luebbe 	}
133c5dbcf8bSJan Luebbe 
134c5dbcf8bSJan Luebbe 	/* IRQ setup */
1354461d651STom Burkart 	ret = gpiod_to_irq(data->gpio_pin);
136c5dbcf8bSJan Luebbe 	if (ret < 0) {
137c5dbcf8bSJan Luebbe 		dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret);
138c5dbcf8bSJan Luebbe 		return -EINVAL;
139c5dbcf8bSJan Luebbe 	}
140c5dbcf8bSJan Luebbe 	data->irq = ret;
141c5dbcf8bSJan Luebbe 
14216152045SJames Nuss 	/* initialize PPS specific parts of the bookkeeping data structure. */
14316152045SJames Nuss 	data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
14416152045SJames Nuss 		PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
145c5dbcf8bSJan Luebbe 	if (data->capture_clear)
14616152045SJames Nuss 		data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
14716152045SJames Nuss 			PPS_ECHOCLEAR;
14816152045SJames Nuss 	data->info.owner = THIS_MODULE;
14916152045SJames Nuss 	snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
15016152045SJames Nuss 		 pdev->name, pdev->id);
15116152045SJames Nuss 
15216152045SJames Nuss 	/* register PPS source */
15316152045SJames Nuss 	pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
154c5dbcf8bSJan Luebbe 	if (data->capture_clear)
15516152045SJames Nuss 		pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
15616152045SJames Nuss 	data->pps = pps_register_source(&data->info, pps_default_params);
1573b1ad360SYueHaibing 	if (IS_ERR(data->pps)) {
158c5dbcf8bSJan Luebbe 		dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n",
159c5dbcf8bSJan Luebbe 			data->irq);
1603b1ad360SYueHaibing 		return PTR_ERR(data->pps);
16116152045SJames Nuss 	}
16216152045SJames Nuss 
16316152045SJames Nuss 	/* register IRQ interrupt handler */
164c5dbcf8bSJan Luebbe 	ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler,
165c5dbcf8bSJan Luebbe 			get_irqf_trigger_flags(data), data->info.name, data);
16616152045SJames Nuss 	if (ret) {
16716152045SJames Nuss 		pps_unregister_source(data->pps);
168c5dbcf8bSJan Luebbe 		dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq);
1692a651822SJan Luebbe 		return -EINVAL;
17016152045SJames Nuss 	}
17116152045SJames Nuss 
172c5dbcf8bSJan Luebbe 	dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n",
173c5dbcf8bSJan Luebbe 		 data->irq);
17416152045SJames Nuss 
17516152045SJames Nuss 	return 0;
17616152045SJames Nuss }
17716152045SJames Nuss 
17816152045SJames Nuss static int pps_gpio_remove(struct platform_device *pdev)
17916152045SJames Nuss {
18016152045SJames Nuss 	struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
18116152045SJames Nuss 
18216152045SJames Nuss 	pps_unregister_source(data->pps);
183c5dbcf8bSJan Luebbe 	dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq);
18416152045SJames Nuss 	return 0;
18516152045SJames Nuss }
18616152045SJames Nuss 
187c5dbcf8bSJan Luebbe static const struct of_device_id pps_gpio_dt_ids[] = {
188c5dbcf8bSJan Luebbe 	{ .compatible = "pps-gpio", },
189c5dbcf8bSJan Luebbe 	{ /* sentinel */ }
190c5dbcf8bSJan Luebbe };
191c5dbcf8bSJan Luebbe MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids);
192c5dbcf8bSJan Luebbe 
19316152045SJames Nuss static struct platform_driver pps_gpio_driver = {
19416152045SJames Nuss 	.probe		= pps_gpio_probe,
1950fe763c5SGreg Kroah-Hartman 	.remove		= pps_gpio_remove,
19616152045SJames Nuss 	.driver		= {
19716152045SJames Nuss 		.name	= PPS_GPIO_NAME,
1981a10bd94SSachin Kamat 		.of_match_table	= pps_gpio_dt_ids,
19916152045SJames Nuss 	},
20016152045SJames Nuss };
20116152045SJames Nuss 
20205212be3SJan Luebbe module_platform_driver(pps_gpio_driver);
20316152045SJames Nuss MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
20416152045SJames Nuss MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
20516152045SJames Nuss MODULE_DESCRIPTION("Use GPIO pin as PPS source");
20616152045SJames Nuss MODULE_LICENSE("GPL");
2074461d651STom Burkart MODULE_VERSION("1.1.0");
208