xref: /openbmc/linux/drivers/pps/clients/pps-gpio.c (revision 0fe763c5)
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>
3416152045SJames Nuss #include <linux/gpio.h>
3516152045SJames Nuss #include <linux/list.h>
3616152045SJames Nuss 
3716152045SJames Nuss /* Info for each registered platform device */
3816152045SJames Nuss struct pps_gpio_device_data {
3916152045SJames Nuss 	int irq;			/* IRQ used as PPS source */
4016152045SJames Nuss 	struct pps_device *pps;		/* PPS source device */
4116152045SJames Nuss 	struct pps_source_info info;	/* PPS source information */
4216152045SJames Nuss 	const struct pps_gpio_platform_data *pdata;
4316152045SJames Nuss };
4416152045SJames Nuss 
4516152045SJames Nuss /*
4616152045SJames Nuss  * Report the PPS event
4716152045SJames Nuss  */
4816152045SJames Nuss 
4916152045SJames Nuss static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
5016152045SJames Nuss {
5116152045SJames Nuss 	const struct pps_gpio_device_data *info;
5216152045SJames Nuss 	struct pps_event_time ts;
5316152045SJames Nuss 	int rising_edge;
5416152045SJames Nuss 
5516152045SJames Nuss 	/* Get the time stamp first */
5616152045SJames Nuss 	pps_get_ts(&ts);
5716152045SJames Nuss 
5816152045SJames Nuss 	info = data;
5916152045SJames Nuss 
6016152045SJames Nuss 	rising_edge = gpio_get_value(info->pdata->gpio_pin);
6116152045SJames Nuss 	if ((rising_edge && !info->pdata->assert_falling_edge) ||
6216152045SJames Nuss 			(!rising_edge && info->pdata->assert_falling_edge))
6316152045SJames Nuss 		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
6416152045SJames Nuss 	else if (info->pdata->capture_clear &&
6516152045SJames Nuss 			((rising_edge && info->pdata->assert_falling_edge) ||
6616152045SJames Nuss 			 (!rising_edge && !info->pdata->assert_falling_edge)))
6716152045SJames Nuss 		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
6816152045SJames Nuss 
6916152045SJames Nuss 	return IRQ_HANDLED;
7016152045SJames Nuss }
7116152045SJames Nuss 
7216152045SJames Nuss static int pps_gpio_setup(struct platform_device *pdev)
7316152045SJames Nuss {
7416152045SJames Nuss 	int ret;
7516152045SJames Nuss 	const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
7616152045SJames Nuss 
7716152045SJames Nuss 	ret = gpio_request(pdata->gpio_pin, pdata->gpio_label);
7816152045SJames Nuss 	if (ret) {
7916152045SJames Nuss 		pr_warning("failed to request GPIO %u\n", pdata->gpio_pin);
8016152045SJames Nuss 		return -EINVAL;
8116152045SJames Nuss 	}
8216152045SJames Nuss 
8316152045SJames Nuss 	ret = gpio_direction_input(pdata->gpio_pin);
8416152045SJames Nuss 	if (ret) {
8516152045SJames Nuss 		pr_warning("failed to set pin direction\n");
8616152045SJames Nuss 		gpio_free(pdata->gpio_pin);
8716152045SJames Nuss 		return -EINVAL;
8816152045SJames Nuss 	}
8916152045SJames Nuss 
9016152045SJames Nuss 	return 0;
9116152045SJames Nuss }
9216152045SJames Nuss 
9316152045SJames Nuss static unsigned long
9416152045SJames Nuss get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata)
9516152045SJames Nuss {
9616152045SJames Nuss 	unsigned long flags = pdata->assert_falling_edge ?
9716152045SJames Nuss 		IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
9816152045SJames Nuss 
9916152045SJames Nuss 	if (pdata->capture_clear) {
10016152045SJames Nuss 		flags |= ((flags & IRQF_TRIGGER_RISING) ?
10116152045SJames Nuss 				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
10216152045SJames Nuss 	}
10316152045SJames Nuss 
10416152045SJames Nuss 	return flags;
10516152045SJames Nuss }
10616152045SJames Nuss 
10716152045SJames Nuss static int pps_gpio_probe(struct platform_device *pdev)
10816152045SJames Nuss {
10916152045SJames Nuss 	struct pps_gpio_device_data *data;
11016152045SJames Nuss 	int irq;
11116152045SJames Nuss 	int ret;
11216152045SJames Nuss 	int err;
11316152045SJames Nuss 	int pps_default_params;
11416152045SJames Nuss 	const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
11516152045SJames Nuss 
11616152045SJames Nuss 
11716152045SJames Nuss 	/* GPIO setup */
11816152045SJames Nuss 	ret = pps_gpio_setup(pdev);
11916152045SJames Nuss 	if (ret)
12016152045SJames Nuss 		return -EINVAL;
12116152045SJames Nuss 
12216152045SJames Nuss 	/* IRQ setup */
12316152045SJames Nuss 	irq = gpio_to_irq(pdata->gpio_pin);
12416152045SJames Nuss 	if (irq < 0) {
12516152045SJames Nuss 		pr_err("failed to map GPIO to IRQ: %d\n", irq);
12616152045SJames Nuss 		err = -EINVAL;
12716152045SJames Nuss 		goto return_error;
12816152045SJames Nuss 	}
12916152045SJames Nuss 
13016152045SJames Nuss 	/* allocate space for device info */
13116152045SJames Nuss 	data = kzalloc(sizeof(struct pps_gpio_device_data), GFP_KERNEL);
13216152045SJames Nuss 	if (data == NULL) {
13316152045SJames Nuss 		err = -ENOMEM;
13416152045SJames Nuss 		goto return_error;
13516152045SJames Nuss 	}
13616152045SJames Nuss 
13716152045SJames Nuss 	/* initialize PPS specific parts of the bookkeeping data structure. */
13816152045SJames Nuss 	data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
13916152045SJames Nuss 		PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
14016152045SJames Nuss 	if (pdata->capture_clear)
14116152045SJames Nuss 		data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
14216152045SJames Nuss 			PPS_ECHOCLEAR;
14316152045SJames Nuss 	data->info.owner = THIS_MODULE;
14416152045SJames Nuss 	snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
14516152045SJames Nuss 		 pdev->name, pdev->id);
14616152045SJames Nuss 
14716152045SJames Nuss 	/* register PPS source */
14816152045SJames Nuss 	pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
14916152045SJames Nuss 	if (pdata->capture_clear)
15016152045SJames Nuss 		pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
15116152045SJames Nuss 	data->pps = pps_register_source(&data->info, pps_default_params);
15216152045SJames Nuss 	if (data->pps == NULL) {
15316152045SJames Nuss 		kfree(data);
15416152045SJames Nuss 		pr_err("failed to register IRQ %d as PPS source\n", irq);
15516152045SJames Nuss 		err = -EINVAL;
15616152045SJames Nuss 		goto return_error;
15716152045SJames Nuss 	}
15816152045SJames Nuss 
15916152045SJames Nuss 	data->irq = irq;
16016152045SJames Nuss 	data->pdata = pdata;
16116152045SJames Nuss 
16216152045SJames Nuss 	/* register IRQ interrupt handler */
16316152045SJames Nuss 	ret = request_irq(irq, pps_gpio_irq_handler,
16416152045SJames Nuss 			get_irqf_trigger_flags(pdata), data->info.name, data);
16516152045SJames Nuss 	if (ret) {
16616152045SJames Nuss 		pps_unregister_source(data->pps);
16716152045SJames Nuss 		kfree(data);
16816152045SJames Nuss 		pr_err("failed to acquire IRQ %d\n", irq);
16916152045SJames Nuss 		err = -EINVAL;
17016152045SJames Nuss 		goto return_error;
17116152045SJames Nuss 	}
17216152045SJames Nuss 
17316152045SJames Nuss 	platform_set_drvdata(pdev, data);
17416152045SJames Nuss 	dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq);
17516152045SJames Nuss 
17616152045SJames Nuss 	return 0;
17716152045SJames Nuss 
17816152045SJames Nuss return_error:
17916152045SJames Nuss 	gpio_free(pdata->gpio_pin);
18016152045SJames Nuss 	return err;
18116152045SJames Nuss }
18216152045SJames Nuss 
18316152045SJames Nuss static int pps_gpio_remove(struct platform_device *pdev)
18416152045SJames Nuss {
18516152045SJames Nuss 	struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
18616152045SJames Nuss 	const struct pps_gpio_platform_data *pdata = data->pdata;
18716152045SJames Nuss 
18816152045SJames Nuss 	platform_set_drvdata(pdev, NULL);
18916152045SJames Nuss 	free_irq(data->irq, data);
19016152045SJames Nuss 	gpio_free(pdata->gpio_pin);
19116152045SJames Nuss 	pps_unregister_source(data->pps);
19216152045SJames Nuss 	pr_info("removed IRQ %d as PPS source\n", data->irq);
19316152045SJames Nuss 	kfree(data);
19416152045SJames Nuss 	return 0;
19516152045SJames Nuss }
19616152045SJames Nuss 
19716152045SJames Nuss static struct platform_driver pps_gpio_driver = {
19816152045SJames Nuss 	.probe		= pps_gpio_probe,
1990fe763c5SGreg Kroah-Hartman 	.remove		= pps_gpio_remove,
20016152045SJames Nuss 	.driver		= {
20116152045SJames Nuss 		.name	= PPS_GPIO_NAME,
20216152045SJames Nuss 		.owner	= THIS_MODULE
20316152045SJames Nuss 	},
20416152045SJames Nuss };
20516152045SJames Nuss 
20616152045SJames Nuss static int __init pps_gpio_init(void)
20716152045SJames Nuss {
20816152045SJames Nuss 	int ret = platform_driver_register(&pps_gpio_driver);
20916152045SJames Nuss 	if (ret < 0)
21016152045SJames Nuss 		pr_err("failed to register platform driver\n");
21116152045SJames Nuss 	return ret;
21216152045SJames Nuss }
21316152045SJames Nuss 
21416152045SJames Nuss static void __exit pps_gpio_exit(void)
21516152045SJames Nuss {
21616152045SJames Nuss 	platform_driver_unregister(&pps_gpio_driver);
21716152045SJames Nuss 	pr_debug("unregistered platform driver\n");
21816152045SJames Nuss }
21916152045SJames Nuss 
22016152045SJames Nuss module_init(pps_gpio_init);
22116152045SJames Nuss module_exit(pps_gpio_exit);
22216152045SJames Nuss 
22316152045SJames Nuss MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
22416152045SJames Nuss MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
22516152045SJames Nuss MODULE_DESCRIPTION("Use GPIO pin as PPS source");
22616152045SJames Nuss MODULE_LICENSE("GPL");
22716152045SJames Nuss MODULE_VERSION("1.0.0");
228