2019-05-20 09:19:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-01-12 17:00:58 -08:00
/*
* pps_parport . c - - kernel parallel port PPS client
*
* Copyright ( C ) 2009 Alexander Gordeev < lasaine @ lvk . cs . msu . su >
*/
/*
* TODO :
* implement echo over SEL pin
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/irqnr.h>
# include <linux/time.h>
2013-04-11 23:51:01 +01:00
# include <linux/slab.h>
2011-01-12 17:00:58 -08:00
# include <linux/parport.h>
# include <linux/pps_kernel.h>
# define DRVDESC "parallel port PPS client"
/* module parameters */
# define CLEAR_WAIT_MAX 100
# define CLEAR_WAIT_MAX_ERRORS 5
static unsigned int clear_wait = 100 ;
MODULE_PARM_DESC ( clear_wait ,
" Maximum number of port reads when polling for signal clear, "
" zero turns clear edge capture off entirely " ) ;
module_param ( clear_wait , uint , 0 ) ;
2018-01-28 23:22:08 +00:00
static DEFINE_IDA ( pps_client_index ) ;
2011-01-12 17:00:58 -08:00
/* internal per port structure */
struct pps_client_pp {
struct pardevice * pardev ; /* parport device */
struct pps_device * pps ; /* PPS device */
unsigned int cw ; /* port clear timeout */
unsigned int cw_err ; /* number of timeouts */
2018-01-28 23:22:08 +00:00
int index ; /* device number */
2011-01-12 17:00:58 -08:00
} ;
static inline int signal_is_set ( struct parport * port )
{
return ( port - > ops - > read_status ( port ) & PARPORT_STATUS_ACK ) ! = 0 ;
}
/* parport interrupt handler */
static void parport_irq ( void * handle )
{
struct pps_event_time ts_assert , ts_clear ;
struct pps_client_pp * dev = handle ;
struct parport * port = dev - > pardev - > port ;
unsigned int i ;
unsigned long flags ;
/* first of all we get the time stamp... */
pps_get_ts ( & ts_assert ) ;
if ( dev - > cw = = 0 )
/* clear edge capture disabled */
goto out_assert ;
/* try capture the clear edge */
/* We have to disable interrupts here. The idea is to prevent
* other interrupts on the same processor to introduce random
* lags while polling the port . Reading from IO port is known
* to take approximately 1u s while other interrupt handlers can
* take much more potentially .
*
* Interrupts won ' t be disabled for a long time because the
* number of polls is limited by clear_wait parameter which is
* kept rather low . So it should never be an issue .
*/
local_irq_save ( flags ) ;
/* check the signal (no signal means the pulse is lost this time) */
if ( ! signal_is_set ( port ) ) {
local_irq_restore ( flags ) ;
dev_err ( dev - > pps - > dev , " lost the signal \n " ) ;
goto out_assert ;
}
/* poll the port until the signal is unset */
for ( i = dev - > cw ; i ; i - - )
if ( ! signal_is_set ( port ) ) {
pps_get_ts ( & ts_clear ) ;
local_irq_restore ( flags ) ;
dev - > cw_err = 0 ;
goto out_both ;
}
local_irq_restore ( flags ) ;
/* timeout */
dev - > cw_err + + ;
if ( dev - > cw_err > = CLEAR_WAIT_MAX_ERRORS ) {
dev_err ( dev - > pps - > dev , " disabled clear edge capture after %d "
" timeouts \n " , dev - > cw_err ) ;
dev - > cw = 0 ;
dev - > cw_err = 0 ;
}
out_assert :
/* fire assert event */
pps_event ( dev - > pps , & ts_assert ,
PPS_CAPTUREASSERT , NULL ) ;
return ;
out_both :
/* fire assert event */
pps_event ( dev - > pps , & ts_assert ,
PPS_CAPTUREASSERT , NULL ) ;
/* fire clear event */
pps_event ( dev - > pps , & ts_clear ,
PPS_CAPTURECLEAR , NULL ) ;
return ;
}
static void parport_attach ( struct parport * port )
{
2018-01-28 23:22:08 +00:00
struct pardev_cb pps_client_cb ;
int index ;
2011-01-12 17:00:58 -08:00
struct pps_client_pp * device ;
struct pps_source_info info = {
. name = KBUILD_MODNAME ,
. path = " " ,
. mode = PPS_CAPTUREBOTH | \
PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
PPS_ECHOASSERT | PPS_ECHOCLEAR | \
PPS_CANWAIT | PPS_TSFMT_TSPEC ,
. owner = THIS_MODULE ,
. dev = NULL
} ;
device = kzalloc ( sizeof ( struct pps_client_pp ) , GFP_KERNEL ) ;
if ( ! device ) {
pr_err ( " memory allocation failed, not attaching \n " ) ;
return ;
}
2018-01-28 23:22:08 +00:00
index = ida_simple_get ( & pps_client_index , 0 , 0 , GFP_KERNEL ) ;
memset ( & pps_client_cb , 0 , sizeof ( pps_client_cb ) ) ;
pps_client_cb . private = device ;
pps_client_cb . irq_func = parport_irq ;
pps_client_cb . flags = PARPORT_FLAG_EXCL ;
device - > pardev = parport_register_dev_model ( port ,
KBUILD_MODNAME ,
& pps_client_cb ,
index ) ;
2011-01-12 17:00:58 -08:00
if ( ! device - > pardev ) {
pr_err ( " couldn't register with %s \n " , port - > name ) ;
goto err_free ;
}
if ( parport_claim_or_block ( device - > pardev ) < 0 ) {
pr_err ( " couldn't claim %s \n " , port - > name ) ;
goto err_unregister_dev ;
}
device - > pps = pps_register_source ( & info ,
PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR ) ;
2018-11-26 18:24:22 +08:00
if ( IS_ERR ( device - > pps ) ) {
2011-01-12 17:00:58 -08:00
pr_err ( " couldn't register PPS source \n " ) ;
goto err_release_dev ;
}
device - > cw = clear_wait ;
port - > ops - > enable_irq ( port ) ;
2018-01-28 23:22:08 +00:00
device - > index = index ;
2011-01-12 17:00:58 -08:00
pr_info ( " attached to %s \n " , port - > name ) ;
return ;
err_release_dev :
parport_release ( device - > pardev ) ;
err_unregister_dev :
parport_unregister_device ( device - > pardev ) ;
err_free :
2018-01-28 23:22:08 +00:00
ida_simple_remove ( & pps_client_index , index ) ;
2011-01-12 17:00:58 -08:00
kfree ( device ) ;
}
static void parport_detach ( struct parport * port )
{
struct pardevice * pardev = port - > cad ;
struct pps_client_pp * device ;
/* FIXME: oooh, this is ugly! */
2016-07-20 15:45:08 -07:00
if ( ! pardev | | strcmp ( pardev - > name , KBUILD_MODNAME ) )
2011-01-12 17:00:58 -08:00
/* not our port */
return ;
device = pardev - > private ;
port - > ops - > disable_irq ( port ) ;
pps_unregister_source ( device - > pps ) ;
parport_release ( pardev ) ;
parport_unregister_device ( pardev ) ;
2018-01-28 23:22:08 +00:00
ida_simple_remove ( & pps_client_index , device - > index ) ;
2011-01-12 17:00:58 -08:00
kfree ( device ) ;
}
static struct parport_driver pps_parport_driver = {
. name = KBUILD_MODNAME ,
2018-01-28 23:22:08 +00:00
. match_port = parport_attach ,
2011-01-12 17:00:58 -08:00
. detach = parport_detach ,
2018-01-28 23:22:08 +00:00
. devmodel = true ,
2011-01-12 17:00:58 -08:00
} ;
/* module staff */
static int __init pps_parport_init ( void )
{
int ret ;
pr_info ( DRVDESC " \n " ) ;
if ( clear_wait > CLEAR_WAIT_MAX ) {
pr_err ( " clear_wait value should be not greater "
" then %d \n " , CLEAR_WAIT_MAX ) ;
return - EINVAL ;
}
ret = parport_register_driver ( & pps_parport_driver ) ;
if ( ret ) {
pr_err ( " unable to register with parport \n " ) ;
return ret ;
}
return 0 ;
}
static void __exit pps_parport_exit ( void )
{
parport_unregister_driver ( & pps_parport_driver ) ;
}
module_init ( pps_parport_init ) ;
module_exit ( pps_parport_exit ) ;
MODULE_AUTHOR ( " Alexander Gordeev <lasaine@lvk.cs.msu.su> " ) ;
MODULE_DESCRIPTION ( DRVDESC ) ;
MODULE_LICENSE ( " GPL " ) ;