2019-05-20 09:19:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-04-22 12:04:31 +02:00
/*
* PTP 1588 clock using the IXP46X
*
* Copyright ( C ) 2010 OMICRON electronics GmbH
*/
# include <linux/device.h>
2021-08-28 19:15:44 +02:00
# include <linux/module.h>
2011-04-22 12:04:31 +02:00
# include <linux/err.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/ptp_clock_kernel.h>
2021-08-28 19:15:44 +02:00
# include <linux/platform_device.h>
2019-08-25 21:57:25 +02:00
# include <linux/soc/ixp4xx/cpu.h>
2021-07-21 17:19:32 +02:00
# include <linux/module.h>
# include <mach/ixp4xx-regs.h>
2020-01-12 13:04:44 +01:00
# include "ixp46x_ts.h"
2011-04-22 12:04:31 +02:00
# define DRIVER "ptp_ixp46x"
# define N_EXT_TS 2
# define MASTER_GPIO 8
# define SLAVE_GPIO 7
struct ixp_clock {
struct ixp46x_ts_regs * regs ;
struct ptp_clock * ptp_clock ;
struct ptp_clock_info caps ;
int exts0_enabled ;
int exts1_enabled ;
2021-08-28 19:15:44 +02:00
int slave_irq ;
int master_irq ;
2011-04-22 12:04:31 +02:00
} ;
2021-08-28 19:15:44 +02:00
static DEFINE_SPINLOCK ( register_lock ) ;
2011-04-22 12:04:31 +02:00
/*
* Register access functions
*/
static u64 ixp_systime_read ( struct ixp46x_ts_regs * regs )
{
u64 ns ;
u32 lo , hi ;
lo = __raw_readl ( & regs - > systime_lo ) ;
hi = __raw_readl ( & regs - > systime_hi ) ;
ns = ( ( u64 ) hi ) < < 32 ;
ns | = lo ;
ns < < = TICKS_NS_SHIFT ;
return ns ;
}
static void ixp_systime_write ( struct ixp46x_ts_regs * regs , u64 ns )
{
u32 hi , lo ;
ns > > = TICKS_NS_SHIFT ;
hi = ns > > 32 ;
lo = ns & 0xffffffff ;
__raw_writel ( lo , & regs - > systime_lo ) ;
__raw_writel ( hi , & regs - > systime_hi ) ;
}
/*
* Interrupt service routine
*/
static irqreturn_t isr ( int irq , void * priv )
{
struct ixp_clock * ixp_clock = priv ;
struct ixp46x_ts_regs * regs = ixp_clock - > regs ;
struct ptp_clock_event event ;
u32 ack = 0 , lo , hi , val ;
val = __raw_readl ( & regs - > event ) ;
if ( val & TSER_SNS ) {
ack | = TSER_SNS ;
if ( ixp_clock - > exts0_enabled ) {
hi = __raw_readl ( & regs - > asms_hi ) ;
lo = __raw_readl ( & regs - > asms_lo ) ;
event . type = PTP_CLOCK_EXTTS ;
event . index = 0 ;
event . timestamp = ( ( u64 ) hi ) < < 32 ;
event . timestamp | = lo ;
event . timestamp < < = TICKS_NS_SHIFT ;
ptp_clock_event ( ixp_clock - > ptp_clock , & event ) ;
}
}
if ( val & TSER_SNM ) {
ack | = TSER_SNM ;
if ( ixp_clock - > exts1_enabled ) {
hi = __raw_readl ( & regs - > amms_hi ) ;
lo = __raw_readl ( & regs - > amms_lo ) ;
event . type = PTP_CLOCK_EXTTS ;
event . index = 1 ;
event . timestamp = ( ( u64 ) hi ) < < 32 ;
event . timestamp | = lo ;
event . timestamp < < = TICKS_NS_SHIFT ;
ptp_clock_event ( ixp_clock - > ptp_clock , & event ) ;
}
}
if ( val & TTIPEND )
ack | = TTIPEND ; /* this bit seems to be always set */
if ( ack ) {
__raw_writel ( ack , & regs - > event ) ;
return IRQ_HANDLED ;
} else
return IRQ_NONE ;
}
/*
* PTP clock operations
*/
static int ptp_ixp_adjfreq ( struct ptp_clock_info * ptp , s32 ppb )
{
u64 adj ;
u32 diff , addend ;
int neg_adj = 0 ;
struct ixp_clock * ixp_clock = container_of ( ptp , struct ixp_clock , caps ) ;
struct ixp46x_ts_regs * regs = ixp_clock - > regs ;
if ( ppb < 0 ) {
neg_adj = 1 ;
ppb = - ppb ;
}
addend = DEFAULT_ADDEND ;
adj = addend ;
adj * = ppb ;
diff = div_u64 ( adj , 1000000000ULL ) ;
addend = neg_adj ? addend - diff : addend + diff ;
__raw_writel ( addend , & regs - > addend ) ;
return 0 ;
}
static int ptp_ixp_adjtime ( struct ptp_clock_info * ptp , s64 delta )
{
s64 now ;
unsigned long flags ;
struct ixp_clock * ixp_clock = container_of ( ptp , struct ixp_clock , caps ) ;
struct ixp46x_ts_regs * regs = ixp_clock - > regs ;
spin_lock_irqsave ( & register_lock , flags ) ;
now = ixp_systime_read ( regs ) ;
now + = delta ;
ixp_systime_write ( regs , now ) ;
spin_unlock_irqrestore ( & register_lock , flags ) ;
return 0 ;
}
2015-03-29 23:12:11 +02:00
static int ptp_ixp_gettime ( struct ptp_clock_info * ptp , struct timespec64 * ts )
2011-04-22 12:04:31 +02:00
{
u64 ns ;
unsigned long flags ;
struct ixp_clock * ixp_clock = container_of ( ptp , struct ixp_clock , caps ) ;
struct ixp46x_ts_regs * regs = ixp_clock - > regs ;
spin_lock_irqsave ( & register_lock , flags ) ;
ns = ixp_systime_read ( regs ) ;
spin_unlock_irqrestore ( & register_lock , flags ) ;
2016-01-28 10:27:19 +08:00
* ts = ns_to_timespec64 ( ns ) ;
2011-04-22 12:04:31 +02:00
return 0 ;
}
static int ptp_ixp_settime ( struct ptp_clock_info * ptp ,
2015-03-29 23:12:11 +02:00
const struct timespec64 * ts )
2011-04-22 12:04:31 +02:00
{
u64 ns ;
unsigned long flags ;
struct ixp_clock * ixp_clock = container_of ( ptp , struct ixp_clock , caps ) ;
struct ixp46x_ts_regs * regs = ixp_clock - > regs ;
2016-01-28 10:27:19 +08:00
ns = timespec64_to_ns ( ts ) ;
2011-04-22 12:04:31 +02:00
spin_lock_irqsave ( & register_lock , flags ) ;
ixp_systime_write ( regs , ns ) ;
spin_unlock_irqrestore ( & register_lock , flags ) ;
return 0 ;
}
static int ptp_ixp_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
struct ixp_clock * ixp_clock = container_of ( ptp , struct ixp_clock , caps ) ;
switch ( rq - > type ) {
case PTP_CLK_REQ_EXTTS :
switch ( rq - > extts . index ) {
case 0 :
ixp_clock - > exts0_enabled = on ? 1 : 0 ;
break ;
case 1 :
ixp_clock - > exts1_enabled = on ? 1 : 0 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
default :
break ;
}
return - EOPNOTSUPP ;
}
2017-08-21 23:01:12 +05:30
static const struct ptp_clock_info ptp_ixp_caps = {
2011-04-22 12:04:31 +02:00
. owner = THIS_MODULE ,
. name = " IXP46X timer " ,
. max_adj = 66666655 ,
. n_ext_ts = N_EXT_TS ,
2014-03-20 22:21:55 +01:00
. n_pins = 0 ,
2011-04-22 12:04:31 +02:00
. pps = 0 ,
. adjfreq = ptp_ixp_adjfreq ,
. adjtime = ptp_ixp_adjtime ,
2015-03-29 23:12:11 +02:00
. gettime64 = ptp_ixp_gettime ,
. settime64 = ptp_ixp_settime ,
2011-04-22 12:04:31 +02:00
. enable = ptp_ixp_enable ,
} ;
/* module operations */
static struct ixp_clock ixp_clock ;
static int setup_interrupt ( int gpio )
{
int irq ;
2013-09-10 13:15:18 +02:00
int err ;
2011-04-22 12:04:31 +02:00
2013-09-10 13:15:18 +02:00
err = gpio_request ( gpio , " ixp4-ptp " ) ;
if ( err )
return err ;
err = gpio_direction_input ( gpio ) ;
if ( err )
return err ;
2011-04-22 12:04:31 +02:00
irq = gpio_to_irq ( gpio ) ;
2016-09-06 21:20:36 +02:00
if ( irq < 0 )
return irq ;
2011-04-22 12:04:31 +02:00
2016-09-06 21:20:36 +02:00
err = irq_set_irq_type ( irq , IRQF_TRIGGER_FALLING ) ;
if ( err ) {
2011-04-22 12:04:31 +02:00
pr_err ( " cannot set trigger type for irq %d \n " , irq ) ;
2016-09-06 21:20:36 +02:00
return err ;
2011-04-22 12:04:31 +02:00
}
2016-09-06 21:20:36 +02:00
err = request_irq ( irq , isr , 0 , DRIVER , & ixp_clock ) ;
if ( err ) {
2011-04-22 12:04:31 +02:00
pr_err ( " request_irq failed for irq %d \n " , irq ) ;
2016-09-06 21:20:36 +02:00
return err ;
2011-04-22 12:04:31 +02:00
}
return irq ;
}
2021-08-28 19:15:44 +02:00
int ixp46x_ptp_find ( struct ixp46x_ts_regs * __iomem * regs , int * phc_index )
2011-04-22 12:04:31 +02:00
{
2021-08-28 19:15:44 +02:00
* regs = ixp_clock . regs ;
* phc_index = ptp_clock_index ( ixp_clock . ptp_clock ) ;
if ( ! ixp_clock . ptp_clock )
return - EPROBE_DEFER ;
return 0 ;
2011-04-22 12:04:31 +02:00
}
2021-08-28 19:15:44 +02:00
EXPORT_SYMBOL_GPL ( ixp46x_ptp_find ) ;
2011-04-22 12:04:31 +02:00
2021-08-28 19:15:44 +02:00
static int ptp_ixp_remove ( struct platform_device * pdev )
2011-04-22 12:04:31 +02:00
{
2021-08-28 19:15:44 +02:00
free_irq ( ixp_clock . master_irq , & ixp_clock ) ;
free_irq ( ixp_clock . slave_irq , & ixp_clock ) ;
ptp_clock_unregister ( ixp_clock . ptp_clock ) ;
ixp_clock . ptp_clock = NULL ;
2011-04-22 12:04:31 +02:00
2021-08-28 19:15:44 +02:00
return 0 ;
}
static int ptp_ixp_probe ( struct platform_device * pdev )
{
ixp_clock . regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
ixp_clock . master_irq = platform_get_irq ( pdev , 0 ) ;
ixp_clock . slave_irq = platform_get_irq ( pdev , 1 ) ;
if ( IS_ERR ( ixp_clock . regs ) | |
! ixp_clock . master_irq | | ! ixp_clock . slave_irq )
return - ENXIO ;
2011-04-22 12:04:31 +02:00
ixp_clock . caps = ptp_ixp_caps ;
2012-09-22 07:02:03 +00:00
ixp_clock . ptp_clock = ptp_clock_register ( & ixp_clock . caps , NULL ) ;
2011-04-22 12:04:31 +02:00
if ( IS_ERR ( ixp_clock . ptp_clock ) )
return PTR_ERR ( ixp_clock . ptp_clock ) ;
__raw_writel ( DEFAULT_ADDEND , & ixp_clock . regs - > addend ) ;
__raw_writel ( 1 , & ixp_clock . regs - > trgt_lo ) ;
__raw_writel ( 0 , & ixp_clock . regs - > trgt_hi ) ;
__raw_writel ( TTIPEND , & ixp_clock . regs - > event ) ;
2021-08-28 19:15:44 +02:00
if ( ixp_clock . master_irq ! = setup_interrupt ( MASTER_GPIO ) ) {
2011-04-22 12:04:31 +02:00
pr_err ( " failed to setup gpio %d as irq \n " , MASTER_GPIO ) ;
goto no_master ;
}
2021-08-28 19:15:44 +02:00
if ( ixp_clock . slave_irq ! = setup_interrupt ( SLAVE_GPIO ) ) {
2011-04-22 12:04:31 +02:00
pr_err ( " failed to setup gpio %d as irq \n " , SLAVE_GPIO ) ;
goto no_slave ;
}
return 0 ;
no_slave :
2021-08-28 19:15:44 +02:00
free_irq ( ixp_clock . master_irq , & ixp_clock ) ;
2011-04-22 12:04:31 +02:00
no_master :
ptp_clock_unregister ( ixp_clock . ptp_clock ) ;
2021-08-28 19:15:44 +02:00
ixp_clock . ptp_clock = NULL ;
2011-04-22 12:04:31 +02:00
return - ENODEV ;
}
2021-08-28 19:15:44 +02:00
static struct platform_driver ptp_ixp_driver = {
. driver . name = " ptp-ixp46x " ,
. driver . suppress_bind_attrs = true ,
. probe = ptp_ixp_probe ,
. remove = ptp_ixp_remove ,
} ;
module_platform_driver ( ptp_ixp_driver ) ;
2011-04-22 12:04:31 +02:00
2012-03-16 22:39:29 +00:00
MODULE_AUTHOR ( " Richard Cochran <richardcochran@gmail.com> " ) ;
2011-04-22 12:04:31 +02:00
MODULE_DESCRIPTION ( " PTP clock using the IXP46X timer " ) ;
MODULE_LICENSE ( " GPL " ) ;