2019-05-20 09:19:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-11-02 13:39:38 -07:00
/*
* pps - gpio . c - - PPS client driver using GPIO
*
* Copyright ( C ) 2010 Ricardo Martins < rasm @ fe . up . pt >
* Copyright ( C ) 2011 James Nuss < jamesnuss @ nanometrics . ca >
*/
# define PPS_GPIO_NAME "pps-gpio"
# define pr_fmt(fmt) PPS_GPIO_NAME ": " fmt
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
2021-03-18 15:03:19 +02:00
# include <linux/mod_devicetable.h>
2011-11-02 13:39:38 -07:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/pps_kernel.h>
2019-05-14 15:45:40 -07:00
# include <linux/gpio/consumer.h>
2011-11-02 13:39:38 -07:00
# include <linux/list.h>
2021-03-18 15:03:19 +02:00
# include <linux/property.h>
2019-05-14 15:45:46 -07:00
# include <linux/timer.h>
# include <linux/jiffies.h>
2011-11-02 13:39:38 -07:00
/* Info for each registered platform device */
struct pps_gpio_device_data {
int irq ; /* IRQ used as PPS source */
struct pps_device * pps ; /* PPS source device */
struct pps_source_info info ; /* PPS source information */
2019-05-14 15:45:40 -07:00
struct gpio_desc * gpio_pin ; /* GPIO port descriptors */
2019-05-14 15:45:46 -07:00
struct gpio_desc * echo_pin ;
struct timer_list echo_timer ; /* timer to reset echo active state */
2013-07-03 15:09:12 -07:00
bool assert_falling_edge ;
bool capture_clear ;
2019-05-14 15:45:46 -07:00
unsigned int echo_active_ms ; /* PPS echo active duration */
unsigned long echo_timeout ; /* timer timeout value in jiffies */
2011-11-02 13:39:38 -07:00
} ;
/*
* Report the PPS event
*/
static irqreturn_t pps_gpio_irq_handler ( int irq , void * data )
{
const struct pps_gpio_device_data * info ;
struct pps_event_time ts ;
int rising_edge ;
/* Get the time stamp first */
pps_get_ts ( & ts ) ;
info = data ;
2019-05-14 15:45:40 -07:00
rising_edge = gpiod_get_value ( info - > gpio_pin ) ;
2013-07-03 15:09:12 -07:00
if ( ( rising_edge & & ! info - > assert_falling_edge ) | |
( ! rising_edge & & info - > assert_falling_edge ) )
2019-05-14 15:45:46 -07:00
pps_event ( info - > pps , & ts , PPS_CAPTUREASSERT , data ) ;
2013-07-03 15:09:12 -07:00
else if ( info - > capture_clear & &
( ( rising_edge & & info - > assert_falling_edge ) | |
2019-05-14 15:45:40 -07:00
( ! rising_edge & & ! info - > assert_falling_edge ) ) )
2019-05-14 15:45:46 -07:00
pps_event ( info - > pps , & ts , PPS_CAPTURECLEAR , data ) ;
2011-11-02 13:39:38 -07:00
return IRQ_HANDLED ;
}
2019-05-14 15:45:46 -07:00
/* This function will only be called when an ECHO GPIO is defined */
static void pps_gpio_echo ( struct pps_device * pps , int event , void * data )
{
/* add_timer() needs to write into info->echo_timer */
struct pps_gpio_device_data * info = data ;
switch ( event ) {
case PPS_CAPTUREASSERT :
if ( pps - > params . mode & PPS_ECHOASSERT )
gpiod_set_value ( info - > echo_pin , 1 ) ;
break ;
case PPS_CAPTURECLEAR :
if ( pps - > params . mode & PPS_ECHOCLEAR )
gpiod_set_value ( info - > echo_pin , 1 ) ;
break ;
}
/* fire the timer */
if ( info - > pps - > params . mode & ( PPS_ECHOASSERT | PPS_ECHOCLEAR ) ) {
info - > echo_timer . expires = jiffies + info - > echo_timeout ;
add_timer ( & info - > echo_timer ) ;
}
}
/* Timer callback to reset the echo pin to the inactive state */
static void pps_gpio_echo_timer_callback ( struct timer_list * t )
{
const struct pps_gpio_device_data * info ;
info = from_timer ( info , t , echo_timer ) ;
gpiod_set_value ( info - > echo_pin , 0 ) ;
}
2021-03-18 15:03:20 +02:00
static int pps_gpio_setup ( struct device * dev )
2019-05-14 15:45:40 -07:00
{
2021-03-18 15:03:20 +02:00
struct pps_gpio_device_data * data = dev_get_drvdata ( dev ) ;
2019-05-14 15:45:46 -07:00
int ret ;
u32 value ;
2019-05-14 15:45:40 -07:00
2021-03-18 15:03:20 +02:00
data - > gpio_pin = devm_gpiod_get ( dev , NULL , GPIOD_IN ) ;
2021-03-18 15:03:16 +02:00
if ( IS_ERR ( data - > gpio_pin ) )
2021-03-18 15:03:20 +02:00
return dev_err_probe ( dev , PTR_ERR ( data - > gpio_pin ) ,
2021-03-18 15:03:16 +02:00
" failed to request PPS GPIO \n " ) ;
2019-05-14 15:45:40 -07:00
2021-03-18 15:03:21 +02:00
data - > assert_falling_edge =
device_property_read_bool ( dev , " assert-falling-edge " ) ;
2021-03-18 15:03:20 +02:00
data - > echo_pin = devm_gpiod_get_optional ( dev , " echo " , GPIOD_OUT_LOW ) ;
2021-03-18 15:03:16 +02:00
if ( IS_ERR ( data - > echo_pin ) )
2021-03-18 15:03:20 +02:00
return dev_err_probe ( dev , PTR_ERR ( data - > echo_pin ) ,
2021-03-18 15:03:16 +02:00
" failed to request ECHO GPIO \n " ) ;
2019-05-14 15:45:46 -07:00
2021-03-18 15:03:21 +02:00
if ( ! data - > echo_pin )
return 0 ;
ret = device_property_read_u32 ( dev , " echo-active-ms " , & value ) ;
if ( ret ) {
dev_err ( dev , " failed to get echo-active-ms from FW \n " ) ;
return ret ;
2019-05-14 15:45:46 -07:00
}
2021-03-18 15:03:21 +02:00
/* sanity check on echo_active_ms */
if ( ! value | | value > 999 ) {
dev_err ( dev , " echo-active-ms: %u - bad value from FW \n " , value ) ;
return - EINVAL ;
}
data - > echo_active_ms = value ;
2019-05-14 15:45:40 -07:00
return 0 ;
}
2011-11-02 13:39:38 -07:00
static unsigned long
2013-07-03 15:09:12 -07:00
get_irqf_trigger_flags ( const struct pps_gpio_device_data * data )
2011-11-02 13:39:38 -07:00
{
2013-07-03 15:09:12 -07:00
unsigned long flags = data - > assert_falling_edge ?
2011-11-02 13:39:38 -07:00
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING ;
2013-07-03 15:09:12 -07:00
if ( data - > capture_clear ) {
2011-11-02 13:39:38 -07:00
flags | = ( ( flags & IRQF_TRIGGER_RISING ) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING ) ;
}
return flags ;
}
static int pps_gpio_probe ( struct platform_device * pdev )
{
struct pps_gpio_device_data * data ;
2021-03-18 15:03:20 +02:00
struct device * dev = & pdev - > dev ;
2011-11-02 13:39:38 -07:00
int ret ;
int pps_default_params ;
2013-07-03 15:09:12 -07:00
/* allocate space for device info */
2021-03-18 15:03:20 +02:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
2013-07-03 15:09:12 -07:00
if ( ! data )
return - ENOMEM ;
2021-03-18 15:03:20 +02:00
dev_set_drvdata ( dev , data ) ;
2011-11-02 13:39:38 -07:00
2019-05-14 15:45:40 -07:00
/* GPIO setup */
2021-03-18 15:03:20 +02:00
ret = pps_gpio_setup ( dev ) ;
2021-03-18 15:03:18 +02:00
if ( ret )
return - EINVAL ;
2011-11-02 13:39:38 -07:00
/* IRQ setup */
2019-05-14 15:45:40 -07:00
ret = gpiod_to_irq ( data - > gpio_pin ) ;
2013-07-03 15:09:12 -07:00
if ( ret < 0 ) {
2021-03-18 15:03:20 +02:00
dev_err ( dev , " failed to map GPIO to IRQ: %d \n " , ret ) ;
2013-07-03 15:09:10 -07:00
return - EINVAL ;
2011-11-02 13:39:38 -07:00
}
2013-07-03 15:09:12 -07:00
data - > irq = ret ;
2011-11-02 13:39:38 -07:00
/* initialize PPS specific parts of the bookkeeping data structure. */
data - > info . mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC ;
2013-07-03 15:09:12 -07:00
if ( data - > capture_clear )
2011-11-02 13:39:38 -07:00
data - > info . mode | = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
PPS_ECHOCLEAR ;
data - > info . owner = THIS_MODULE ;
snprintf ( data - > info . name , PPS_MAX_NAME_LEN - 1 , " %s.%d " ,
pdev - > name , pdev - > id ) ;
2019-05-14 15:45:46 -07:00
if ( data - > echo_pin ) {
data - > info . echo = pps_gpio_echo ;
data - > echo_timeout = msecs_to_jiffies ( data - > echo_active_ms ) ;
timer_setup ( & data - > echo_timer , pps_gpio_echo_timer_callback , 0 ) ;
}
2011-11-02 13:39:38 -07:00
/* register PPS source */
pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT ;
2013-07-03 15:09:12 -07:00
if ( data - > capture_clear )
2011-11-02 13:39:38 -07:00
pps_default_params | = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR ;
data - > pps = pps_register_source ( & data - > info , pps_default_params ) ;
2018-11-26 18:24:22 +08:00
if ( IS_ERR ( data - > pps ) ) {
2021-03-18 15:03:20 +02:00
dev_err ( dev , " failed to register IRQ %d as PPS source \n " ,
2013-07-03 15:09:12 -07:00
data - > irq ) ;
2018-11-26 18:24:22 +08:00
return PTR_ERR ( data - > pps ) ;
2011-11-02 13:39:38 -07:00
}
/* register IRQ interrupt handler */
2021-03-18 15:03:20 +02:00
ret = devm_request_irq ( dev , data - > irq , pps_gpio_irq_handler ,
2013-07-03 15:09:12 -07:00
get_irqf_trigger_flags ( data ) , data - > info . name , data ) ;
2011-11-02 13:39:38 -07:00
if ( ret ) {
pps_unregister_source ( data - > pps ) ;
2021-03-18 15:03:20 +02:00
dev_err ( dev , " failed to acquire IRQ %d \n " , data - > irq ) ;
2013-07-03 15:09:10 -07:00
return - EINVAL ;
2011-11-02 13:39:38 -07:00
}
2013-07-03 15:09:12 -07:00
dev_info ( data - > pps - > dev , " Registered IRQ %d as PPS source \n " ,
data - > irq ) ;
2011-11-02 13:39:38 -07:00
return 0 ;
}
static int pps_gpio_remove ( struct platform_device * pdev )
{
struct pps_gpio_device_data * data = platform_get_drvdata ( pdev ) ;
pps_unregister_source ( data - > pps ) ;
2021-03-18 15:03:17 +02:00
del_timer_sync ( & data - > echo_timer ) ;
/* reset echo pin in any case */
gpiod_set_value ( data - > echo_pin , 0 ) ;
2013-07-03 15:09:12 -07:00
dev_info ( & pdev - > dev , " removed IRQ %d as PPS source \n " , data - > irq ) ;
2011-11-02 13:39:38 -07:00
return 0 ;
}
2013-07-03 15:09:12 -07:00
static const struct of_device_id pps_gpio_dt_ids [ ] = {
{ . compatible = " pps-gpio " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , pps_gpio_dt_ids ) ;
2011-11-02 13:39:38 -07:00
static struct platform_driver pps_gpio_driver = {
. probe = pps_gpio_probe ,
2012-12-21 15:14:44 -08:00
. remove = pps_gpio_remove ,
2011-11-02 13:39:38 -07:00
. driver = {
. name = PPS_GPIO_NAME ,
2013-11-12 15:11:33 -08:00
. of_match_table = pps_gpio_dt_ids ,
2011-11-02 13:39:38 -07:00
} ,
} ;
2013-07-03 15:09:10 -07:00
module_platform_driver ( pps_gpio_driver ) ;
2011-11-02 13:39:38 -07:00
MODULE_AUTHOR ( " Ricardo Martins <rasm@fe.up.pt> " ) ;
MODULE_AUTHOR ( " James Nuss <jamesnuss@nanometrics.ca> " ) ;
MODULE_DESCRIPTION ( " Use GPIO pin as PPS source " ) ;
MODULE_LICENSE ( " GPL " ) ;
2019-05-14 15:45:46 -07:00
MODULE_VERSION ( " 1.2.0 " ) ;