xref: /openbmc/linux/drivers/pps/clients/pps-gpio.c (revision 2a651822)
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 
772a651822SJan Luebbe 	ret = devm_gpio_request(&pdev->dev, 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 		return -EINVAL;
8716152045SJames Nuss 	}
8816152045SJames Nuss 
8916152045SJames Nuss 	return 0;
9016152045SJames Nuss }
9116152045SJames Nuss 
9216152045SJames Nuss static unsigned long
9316152045SJames Nuss get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata)
9416152045SJames Nuss {
9516152045SJames Nuss 	unsigned long flags = pdata->assert_falling_edge ?
9616152045SJames Nuss 		IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
9716152045SJames Nuss 
9816152045SJames Nuss 	if (pdata->capture_clear) {
9916152045SJames Nuss 		flags |= ((flags & IRQF_TRIGGER_RISING) ?
10016152045SJames Nuss 				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
10116152045SJames Nuss 	}
10216152045SJames Nuss 
10316152045SJames Nuss 	return flags;
10416152045SJames Nuss }
10516152045SJames Nuss 
10616152045SJames Nuss static int pps_gpio_probe(struct platform_device *pdev)
10716152045SJames Nuss {
10816152045SJames Nuss 	struct pps_gpio_device_data *data;
10916152045SJames Nuss 	int irq;
11016152045SJames Nuss 	int ret;
11116152045SJames Nuss 	int pps_default_params;
11216152045SJames Nuss 	const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
11316152045SJames Nuss 
11416152045SJames Nuss 
11516152045SJames Nuss 	/* GPIO setup */
11616152045SJames Nuss 	ret = pps_gpio_setup(pdev);
11716152045SJames Nuss 	if (ret)
11816152045SJames Nuss 		return -EINVAL;
11916152045SJames Nuss 
12016152045SJames Nuss 	/* IRQ setup */
12116152045SJames Nuss 	irq = gpio_to_irq(pdata->gpio_pin);
12216152045SJames Nuss 	if (irq < 0) {
12316152045SJames Nuss 		pr_err("failed to map GPIO to IRQ: %d\n", irq);
1242a651822SJan Luebbe 		return -EINVAL;
12516152045SJames Nuss 	}
12616152045SJames Nuss 
12716152045SJames Nuss 	/* allocate space for device info */
128507063b2SJulia Lawall 	data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data),
129507063b2SJulia Lawall 			    GFP_KERNEL);
1302a651822SJan Luebbe 	if (data == NULL)
1312a651822SJan Luebbe 		return -ENOMEM;
13216152045SJames Nuss 
13316152045SJames Nuss 	/* initialize PPS specific parts of the bookkeeping data structure. */
13416152045SJames Nuss 	data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
13516152045SJames Nuss 		PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
13616152045SJames Nuss 	if (pdata->capture_clear)
13716152045SJames Nuss 		data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
13816152045SJames Nuss 			PPS_ECHOCLEAR;
13916152045SJames Nuss 	data->info.owner = THIS_MODULE;
14016152045SJames Nuss 	snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
14116152045SJames Nuss 		 pdev->name, pdev->id);
14216152045SJames Nuss 
14316152045SJames Nuss 	/* register PPS source */
14416152045SJames Nuss 	pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
14516152045SJames Nuss 	if (pdata->capture_clear)
14616152045SJames Nuss 		pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
14716152045SJames Nuss 	data->pps = pps_register_source(&data->info, pps_default_params);
14816152045SJames Nuss 	if (data->pps == NULL) {
14916152045SJames Nuss 		pr_err("failed to register IRQ %d as PPS source\n", irq);
1502a651822SJan Luebbe 		return -EINVAL;
15116152045SJames Nuss 	}
15216152045SJames Nuss 
15316152045SJames Nuss 	data->irq = irq;
15416152045SJames Nuss 	data->pdata = pdata;
15516152045SJames Nuss 
15616152045SJames Nuss 	/* register IRQ interrupt handler */
1572a651822SJan Luebbe 	ret = devm_request_irq(&pdev->dev, irq, pps_gpio_irq_handler,
15816152045SJames Nuss 			get_irqf_trigger_flags(pdata), data->info.name, data);
15916152045SJames Nuss 	if (ret) {
16016152045SJames Nuss 		pps_unregister_source(data->pps);
16116152045SJames Nuss 		pr_err("failed to acquire IRQ %d\n", irq);
1622a651822SJan Luebbe 		return -EINVAL;
16316152045SJames Nuss 	}
16416152045SJames Nuss 
16516152045SJames Nuss 	platform_set_drvdata(pdev, data);
16616152045SJames Nuss 	dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq);
16716152045SJames Nuss 
16816152045SJames Nuss 	return 0;
16916152045SJames Nuss }
17016152045SJames Nuss 
17116152045SJames Nuss static int pps_gpio_remove(struct platform_device *pdev)
17216152045SJames Nuss {
17316152045SJames Nuss 	struct pps_gpio_device_data *data = platform_get_drvdata(pdev);
17416152045SJames Nuss 
17516152045SJames Nuss 	platform_set_drvdata(pdev, NULL);
17616152045SJames Nuss 	pps_unregister_source(data->pps);
17716152045SJames Nuss 	pr_info("removed IRQ %d as PPS source\n", data->irq);
17816152045SJames Nuss 	return 0;
17916152045SJames Nuss }
18016152045SJames Nuss 
18116152045SJames Nuss static struct platform_driver pps_gpio_driver = {
18216152045SJames Nuss 	.probe		= pps_gpio_probe,
1830fe763c5SGreg Kroah-Hartman 	.remove		= pps_gpio_remove,
18416152045SJames Nuss 	.driver		= {
18516152045SJames Nuss 		.name	= PPS_GPIO_NAME,
18616152045SJames Nuss 		.owner	= THIS_MODULE
18716152045SJames Nuss 	},
18816152045SJames Nuss };
18916152045SJames Nuss 
19016152045SJames Nuss static int __init pps_gpio_init(void)
19116152045SJames Nuss {
19216152045SJames Nuss 	int ret = platform_driver_register(&pps_gpio_driver);
19316152045SJames Nuss 	if (ret < 0)
19416152045SJames Nuss 		pr_err("failed to register platform driver\n");
19516152045SJames Nuss 	return ret;
19616152045SJames Nuss }
19716152045SJames Nuss 
19816152045SJames Nuss static void __exit pps_gpio_exit(void)
19916152045SJames Nuss {
20016152045SJames Nuss 	platform_driver_unregister(&pps_gpio_driver);
20116152045SJames Nuss 	pr_debug("unregistered platform driver\n");
20216152045SJames Nuss }
20316152045SJames Nuss 
20416152045SJames Nuss module_init(pps_gpio_init);
20516152045SJames Nuss module_exit(pps_gpio_exit);
20616152045SJames Nuss 
20716152045SJames Nuss MODULE_AUTHOR("Ricardo Martins <rasm@fe.up.pt>");
20816152045SJames Nuss MODULE_AUTHOR("James Nuss <jamesnuss@nanometrics.ca>");
20916152045SJames Nuss MODULE_DESCRIPTION("Use GPIO pin as PPS source");
21016152045SJames Nuss MODULE_LICENSE("GPL");
21116152045SJames Nuss MODULE_VERSION("1.0.0");
212