2019-05-27 09:55:15 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-03-08 02:16:26 +04:00
/*
* PTP 1588 clock using the EG20T PCH
*
* Copyright ( C ) 2010 OMICRON electronics GmbH
* Copyright ( C ) 2011 - 2012 LAPIS SEMICONDUCTOR Co . , LTD .
*
* This code was derived from the IXP46X driver .
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
2022-02-08 00:07:26 +03:00
# include <linux/io-64-nonatomic-lo-hi.h>
2022-02-08 00:07:27 +03:00
# include <linux/io-64-nonatomic-hi-lo.h>
2012-03-08 02:16:26 +04:00
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/ptp_clock_kernel.h>
2021-03-12 14:09:08 +03:00
# include <linux/ptp_pch.h>
2012-05-16 05:50:17 +04:00
# include <linux/slab.h>
2012-03-08 02:16:26 +04:00
# define STATION_ADDR_LEN 20
# define PCI_DEVICE_ID_PCH_1588 0x8819
# define IO_MEM_BAR 1
# define DEFAULT_ADDEND 0xA0000000
# define TICKS_NS_SHIFT 5
# define N_EXT_TS 2
enum pch_status {
PCH_SUCCESS ,
PCH_INVALIDPARAM ,
PCH_NOTIMESTAMP ,
PCH_INTERRUPTMODEINUSE ,
PCH_FAILED ,
PCH_UNSUPPORTED ,
} ;
2021-03-12 14:09:10 +03:00
/*
2012-03-08 02:16:26 +04:00
* struct pch_ts_regs - IEEE 1588 registers
*/
struct pch_ts_regs {
u32 control ;
u32 event ;
u32 addend ;
u32 accum ;
u32 test ;
u32 ts_compare ;
u32 rsystime_lo ;
u32 rsystime_hi ;
u32 systime_lo ;
u32 systime_hi ;
u32 trgt_lo ;
u32 trgt_hi ;
u32 asms_lo ;
u32 asms_hi ;
u32 amms_lo ;
u32 amms_hi ;
u32 ch_control ;
u32 ch_event ;
u32 tx_snap_lo ;
u32 tx_snap_hi ;
u32 rx_snap_lo ;
u32 rx_snap_hi ;
u32 src_uuid_lo ;
u32 src_uuid_hi ;
u32 can_status ;
u32 can_snap_lo ;
u32 can_snap_hi ;
u32 ts_sel ;
u32 ts_st [ 6 ] ;
u32 reserve1 [ 14 ] ;
u32 stl_max_set_en ;
u32 stl_max_set ;
u32 reserve2 [ 13 ] ;
u32 srst ;
} ;
# define PCH_TSC_RESET (1 << 0)
# define PCH_TSC_TTM_MASK (1 << 1)
# define PCH_TSC_ASMS_MASK (1 << 2)
# define PCH_TSC_AMMS_MASK (1 << 3)
# define PCH_TSC_PPSM_MASK (1 << 4)
# define PCH_TSE_TTIPEND (1 << 1)
# define PCH_TSE_SNS (1 << 2)
# define PCH_TSE_SNM (1 << 3)
# define PCH_TSE_PPS (1 << 4)
# define PCH_CC_MM (1 << 0)
# define PCH_CC_TA (1 << 1)
# define PCH_CC_MODE_SHIFT 16
# define PCH_CC_MODE_MASK 0x001F0000
# define PCH_CC_VERSION (1 << 31)
# define PCH_CE_TXS (1 << 0)
# define PCH_CE_RXS (1 << 1)
# define PCH_CE_OVR (1 << 0)
# define PCH_CE_VAL (1 << 1)
# define PCH_ECS_ETH (1 << 0)
# define PCH_ECS_CAN (1 << 1)
# define PCH_IEEE1588_ETH (1 << 0)
# define PCH_IEEE1588_CAN (1 << 1)
2021-03-12 14:09:10 +03:00
/*
2012-03-08 02:16:26 +04:00
* struct pch_dev - Driver private data
*/
struct pch_dev {
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs ;
2012-03-08 02:16:26 +04:00
struct ptp_clock * ptp_clock ;
struct ptp_clock_info caps ;
int exts0_enabled ;
int exts1_enabled ;
u32 irq ;
struct pci_dev * pdev ;
spinlock_t register_lock ;
} ;
2021-03-12 14:09:10 +03:00
/*
2012-03-08 02:16:26 +04:00
* struct pch_params - 1588 module parameter
*/
struct pch_params {
u8 station [ STATION_ADDR_LEN ] ;
} ;
/* structure to hold the module parameters */
static struct pch_params pch_param = {
" 00:00:00:00:00:00 "
} ;
/*
* Register access functions
*/
static inline void pch_eth_enable_set ( struct pch_dev * chip )
{
u32 val ;
/* SET the eth_enable bit */
val = ioread32 ( & chip - > regs - > ts_sel ) | ( PCH_ECS_ETH ) ;
iowrite32 ( val , ( & chip - > regs - > ts_sel ) ) ;
}
2013-03-26 23:07:23 +04:00
static u64 pch_systime_read ( struct pch_ts_regs __iomem * regs )
2012-03-08 02:16:26 +04:00
{
u64 ns ;
2022-02-08 00:07:26 +03:00
ns = ioread64_lo_hi ( & regs - > systime_lo ) ;
2012-03-08 02:16:26 +04:00
2022-02-08 00:07:26 +03:00
return ns < < TICKS_NS_SHIFT ;
2012-03-08 02:16:26 +04:00
}
2013-03-26 23:07:23 +04:00
static void pch_systime_write ( struct pch_ts_regs __iomem * regs , u64 ns )
2012-03-08 02:16:26 +04:00
{
2022-02-08 00:07:26 +03:00
iowrite64_lo_hi ( ns > > TICKS_NS_SHIFT , & regs - > systime_lo ) ;
2012-03-08 02:16:26 +04:00
}
static inline void pch_block_reset ( struct pch_dev * chip )
{
u32 val ;
/* Reset Hardware Assist block */
val = ioread32 ( & chip - > regs - > control ) | PCH_TSC_RESET ;
iowrite32 ( val , ( & chip - > regs - > control ) ) ;
val = val & ~ PCH_TSC_RESET ;
iowrite32 ( val , ( & chip - > regs - > control ) ) ;
}
void pch_ch_control_write ( struct pci_dev * pdev , u32 val )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
iowrite32 ( val , ( & chip - > regs - > ch_control ) ) ;
}
EXPORT_SYMBOL ( pch_ch_control_write ) ;
u32 pch_ch_event_read ( struct pci_dev * pdev )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
u32 val ;
val = ioread32 ( & chip - > regs - > ch_event ) ;
return val ;
}
EXPORT_SYMBOL ( pch_ch_event_read ) ;
void pch_ch_event_write ( struct pci_dev * pdev , u32 val )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
iowrite32 ( val , ( & chip - > regs - > ch_event ) ) ;
}
EXPORT_SYMBOL ( pch_ch_event_write ) ;
u32 pch_src_uuid_lo_read ( struct pci_dev * pdev )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
u32 val ;
val = ioread32 ( & chip - > regs - > src_uuid_lo ) ;
return val ;
}
EXPORT_SYMBOL ( pch_src_uuid_lo_read ) ;
u32 pch_src_uuid_hi_read ( struct pci_dev * pdev )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
u32 val ;
val = ioread32 ( & chip - > regs - > src_uuid_hi ) ;
return val ;
}
EXPORT_SYMBOL ( pch_src_uuid_hi_read ) ;
u64 pch_rx_snap_read ( struct pci_dev * pdev )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
u64 ns ;
2022-02-08 00:07:26 +03:00
ns = ioread64_lo_hi ( & chip - > regs - > rx_snap_lo ) ;
2012-03-08 02:16:26 +04:00
2022-02-08 00:07:26 +03:00
return ns < < TICKS_NS_SHIFT ;
2012-03-08 02:16:26 +04:00
}
EXPORT_SYMBOL ( pch_rx_snap_read ) ;
u64 pch_tx_snap_read ( struct pci_dev * pdev )
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
u64 ns ;
2022-02-08 00:07:26 +03:00
ns = ioread64_lo_hi ( & chip - > regs - > tx_snap_lo ) ;
2012-03-08 02:16:26 +04:00
2022-02-08 00:07:26 +03:00
return ns < < TICKS_NS_SHIFT ;
2012-03-08 02:16:26 +04:00
}
EXPORT_SYMBOL ( pch_tx_snap_read ) ;
/* This function enables all 64 bits in system time registers [high & low].
This is a work - around for non continuous value in the SystemTime Register */
static void pch_set_system_time_count ( struct pch_dev * chip )
{
iowrite32 ( 0x01 , & chip - > regs - > stl_max_set_en ) ;
iowrite32 ( 0xFFFFFFFF , & chip - > regs - > stl_max_set ) ;
iowrite32 ( 0x00 , & chip - > regs - > stl_max_set_en ) ;
}
static void pch_reset ( struct pch_dev * chip )
{
/* Reset Hardware Assist */
pch_block_reset ( chip ) ;
/* enable all 32 bits in system time registers */
pch_set_system_time_count ( chip ) ;
}
/**
* pch_set_station_address ( ) - This API sets the station address used by
* IEEE 1588 hardware when looking at PTP
* traffic on the ethernet interface
* @ addr : dress which contain the column separated address to be used .
2021-03-12 14:09:10 +03:00
* @ pdev : PCI device .
2012-03-08 02:16:26 +04:00
*/
2012-04-20 22:50:31 +04:00
int pch_set_station_address ( u8 * addr , struct pci_dev * pdev )
2012-03-08 02:16:26 +04:00
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
2022-02-08 00:07:25 +03:00
bool valid ;
u64 mac ;
2012-03-08 02:16:26 +04:00
/* Verify the parameter */
2013-03-26 23:07:23 +04:00
if ( ( chip - > regs = = NULL ) | | addr = = ( u8 * ) NULL ) {
2012-03-08 02:16:26 +04:00
dev_err ( & pdev - > dev ,
" invalid params returning PCH_INVALIDPARAM \n " ) ;
return PCH_INVALIDPARAM ;
}
2022-02-08 00:07:25 +03:00
valid = mac_pton ( addr , ( u8 * ) & mac ) ;
if ( ! valid ) {
dev_err ( & pdev - > dev , " invalid params returning PCH_INVALIDPARAM \n " ) ;
return PCH_INVALIDPARAM ;
2012-03-08 02:16:26 +04:00
}
2022-02-08 00:07:25 +03:00
dev_dbg ( & pdev - > dev , " invoking pch_station_set \n " ) ;
2022-02-08 00:07:26 +03:00
iowrite64_lo_hi ( mac , & chip - > regs - > ts_st ) ;
2012-03-08 02:16:26 +04:00
return 0 ;
}
2012-04-20 22:50:31 +04:00
EXPORT_SYMBOL ( pch_set_station_address ) ;
2012-03-08 02:16:26 +04:00
/*
* Interrupt service routine
*/
static irqreturn_t isr ( int irq , void * priv )
{
struct pch_dev * pch_dev = priv ;
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs = pch_dev - > regs ;
2012-03-08 02:16:26 +04:00
struct ptp_clock_event event ;
2022-02-08 00:07:27 +03:00
u32 ack = 0 , val ;
2012-03-08 02:16:26 +04:00
val = ioread32 ( & regs - > event ) ;
if ( val & PCH_TSE_SNS ) {
ack | = PCH_TSE_SNS ;
if ( pch_dev - > exts0_enabled ) {
event . type = PTP_CLOCK_EXTTS ;
event . index = 0 ;
2022-02-08 00:07:27 +03:00
event . timestamp = ioread64_hi_lo ( & regs - > asms_hi ) ;
2012-03-08 02:16:26 +04:00
event . timestamp < < = TICKS_NS_SHIFT ;
ptp_clock_event ( pch_dev - > ptp_clock , & event ) ;
}
}
if ( val & PCH_TSE_SNM ) {
ack | = PCH_TSE_SNM ;
if ( pch_dev - > exts1_enabled ) {
event . type = PTP_CLOCK_EXTTS ;
event . index = 1 ;
2022-02-08 00:07:27 +03:00
event . timestamp = ioread64_hi_lo ( & regs - > asms_hi ) ;
2012-03-08 02:16:26 +04:00
event . timestamp < < = TICKS_NS_SHIFT ;
ptp_clock_event ( pch_dev - > ptp_clock , & event ) ;
}
}
if ( val & PCH_TSE_TTIPEND )
ack | = PCH_TSE_TTIPEND ; /* this bit seems to be always set */
if ( ack ) {
iowrite32 ( ack , & regs - > event ) ;
return IRQ_HANDLED ;
} else
return IRQ_NONE ;
}
/*
* PTP clock operations
*/
2022-11-10 02:09:37 +03:00
static int ptp_pch_adjfine ( struct ptp_clock_info * ptp , long scaled_ppm )
2012-03-08 02:16:26 +04:00
{
2022-11-10 02:09:37 +03:00
u32 addend ;
2012-03-08 02:16:26 +04:00
struct pch_dev * pch_dev = container_of ( ptp , struct pch_dev , caps ) ;
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs = pch_dev - > regs ;
2012-03-08 02:16:26 +04:00
2022-11-10 02:09:37 +03:00
addend = adjust_by_scaled_ppm ( DEFAULT_ADDEND , scaled_ppm ) ;
2012-03-08 02:16:26 +04:00
iowrite32 ( addend , & regs - > addend ) ;
return 0 ;
}
static int ptp_pch_adjtime ( struct ptp_clock_info * ptp , s64 delta )
{
s64 now ;
unsigned long flags ;
struct pch_dev * pch_dev = container_of ( ptp , struct pch_dev , caps ) ;
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs = pch_dev - > regs ;
2012-03-08 02:16:26 +04:00
spin_lock_irqsave ( & pch_dev - > register_lock , flags ) ;
now = pch_systime_read ( regs ) ;
now + = delta ;
pch_systime_write ( regs , now ) ;
spin_unlock_irqrestore ( & pch_dev - > register_lock , flags ) ;
return 0 ;
}
2015-03-30 00:12:12 +03:00
static int ptp_pch_gettime ( struct ptp_clock_info * ptp , struct timespec64 * ts )
2012-03-08 02:16:26 +04:00
{
u64 ns ;
unsigned long flags ;
struct pch_dev * pch_dev = container_of ( ptp , struct pch_dev , caps ) ;
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs = pch_dev - > regs ;
2012-03-08 02:16:26 +04:00
spin_lock_irqsave ( & pch_dev - > register_lock , flags ) ;
ns = pch_systime_read ( regs ) ;
spin_unlock_irqrestore ( & pch_dev - > register_lock , flags ) ;
2018-04-27 10:36:18 +03:00
* ts = ns_to_timespec64 ( ns ) ;
2012-03-08 02:16:26 +04:00
return 0 ;
}
static int ptp_pch_settime ( struct ptp_clock_info * ptp ,
2015-03-30 00:12:12 +03:00
const struct timespec64 * ts )
2012-03-08 02:16:26 +04:00
{
u64 ns ;
unsigned long flags ;
struct pch_dev * pch_dev = container_of ( ptp , struct pch_dev , caps ) ;
2013-03-26 23:07:23 +04:00
struct pch_ts_regs __iomem * regs = pch_dev - > regs ;
2012-03-08 02:16:26 +04:00
2018-04-27 10:36:18 +03:00
ns = timespec64_to_ns ( ts ) ;
2012-03-08 02:16:26 +04:00
spin_lock_irqsave ( & pch_dev - > register_lock , flags ) ;
pch_systime_write ( regs , ns ) ;
spin_unlock_irqrestore ( & pch_dev - > register_lock , flags ) ;
return 0 ;
}
static int ptp_pch_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
struct pch_dev * pch_dev = container_of ( ptp , struct pch_dev , caps ) ;
switch ( rq - > type ) {
case PTP_CLK_REQ_EXTTS :
switch ( rq - > extts . index ) {
case 0 :
pch_dev - > exts0_enabled = on ? 1 : 0 ;
break ;
case 1 :
pch_dev - > exts1_enabled = on ? 1 : 0 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
default :
break ;
}
return - EOPNOTSUPP ;
}
2017-08-21 20:31:12 +03:00
static const struct ptp_clock_info ptp_pch_caps = {
2012-03-08 02:16:26 +04:00
. owner = THIS_MODULE ,
. name = " PCH timer " ,
. max_adj = 50000000 ,
. n_ext_ts = N_EXT_TS ,
2014-03-21 01:21:55 +04:00
. n_pins = 0 ,
2012-03-08 02:16:26 +04:00
. pps = 0 ,
2022-11-10 02:09:37 +03:00
. adjfine = ptp_pch_adjfine ,
2012-03-08 02:16:26 +04:00
. adjtime = ptp_pch_adjtime ,
2015-03-30 00:12:12 +03:00
. gettime64 = ptp_pch_gettime ,
. settime64 = ptp_pch_settime ,
2012-03-08 02:16:26 +04:00
. enable = ptp_pch_enable ,
} ;
2012-11-19 22:26:08 +04:00
static void pch_remove ( struct pci_dev * pdev )
2012-03-08 02:16:26 +04:00
{
struct pch_dev * chip = pci_get_drvdata ( pdev ) ;
2022-02-08 00:07:29 +03:00
free_irq ( pdev - > irq , chip ) ;
2012-03-08 02:16:26 +04:00
ptp_clock_unregister ( chip - > ptp_clock ) ;
}
2012-11-19 22:23:28 +04:00
static s32
2012-03-08 02:16:26 +04:00
pch_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
s32 ret ;
unsigned long flags ;
struct pch_dev * chip ;
2022-02-08 00:07:29 +03:00
chip = devm_kzalloc ( & pdev - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
2012-03-08 02:16:26 +04:00
if ( chip = = NULL )
return - ENOMEM ;
/* enable the 1588 pci device */
2022-02-08 00:07:29 +03:00
ret = pcim_enable_device ( pdev ) ;
2012-03-08 02:16:26 +04:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " could not enable the pci device \n " ) ;
2022-02-08 00:07:29 +03:00
return ret ;
2012-03-08 02:16:26 +04:00
}
2022-02-08 00:07:29 +03:00
ret = pcim_iomap_regions ( pdev , BIT ( IO_MEM_BAR ) , " 1588_regs " ) ;
if ( ret ) {
2012-03-08 02:16:26 +04:00
dev_err ( & pdev - > dev , " could not locate IO memory address \n " ) ;
2022-02-08 00:07:29 +03:00
return ret ;
2012-03-08 02:16:26 +04:00
}
/* get the virtual address to the 1588 registers */
2022-02-08 00:07:29 +03:00
chip - > regs = pcim_iomap_table ( pdev ) [ IO_MEM_BAR ] ;
2012-03-08 02:16:26 +04:00
chip - > caps = ptp_pch_caps ;
2012-09-22 11:02:03 +04:00
chip - > ptp_clock = ptp_clock_register ( & chip - > caps , & pdev - > dev ) ;
2022-02-08 00:07:29 +03:00
if ( IS_ERR ( chip - > ptp_clock ) )
return PTR_ERR ( chip - > ptp_clock ) ;
2012-03-08 02:16:26 +04:00
spin_lock_init ( & chip - > register_lock ) ;
ret = request_irq ( pdev - > irq , & isr , IRQF_SHARED , KBUILD_MODNAME , chip ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " failed to get irq %d \n " , pdev - > irq ) ;
goto err_req_irq ;
}
/* indicate success */
chip - > irq = pdev - > irq ;
chip - > pdev = pdev ;
pci_set_drvdata ( pdev , chip ) ;
spin_lock_irqsave ( & chip - > register_lock , flags ) ;
/* reset the ieee1588 h/w */
pch_reset ( chip ) ;
iowrite32 ( DEFAULT_ADDEND , & chip - > regs - > addend ) ;
2022-02-08 00:07:26 +03:00
iowrite64_lo_hi ( 1 , & chip - > regs - > trgt_lo ) ;
2012-03-08 02:16:26 +04:00
iowrite32 ( PCH_TSE_TTIPEND , & chip - > regs - > event ) ;
pch_eth_enable_set ( chip ) ;
if ( strcmp ( pch_param . station , " 00:00:00:00:00:00 " ) ! = 0 ) {
if ( pch_set_station_address ( pch_param . station , pdev ) ! = 0 ) {
dev_err ( & pdev - > dev ,
" Invalid station address parameter \n "
" Module loaded but station address not set correctly \n "
) ;
}
}
spin_unlock_irqrestore ( & chip - > register_lock , flags ) ;
return 0 ;
err_req_irq :
ptp_clock_unregister ( chip - > ptp_clock ) ;
dev_err ( & pdev - > dev , " probe failed(ret=0x%x) \n " , ret ) ;
return ret ;
}
2014-08-08 17:56:03 +04:00
static const struct pci_device_id pch_ieee1588_pcidev_id [ ] = {
2012-03-08 02:16:26 +04:00
{
. vendor = PCI_VENDOR_ID_INTEL ,
. device = PCI_DEVICE_ID_PCH_1588
} ,
{ 0 }
} ;
2021-10-01 19:20:33 +03:00
MODULE_DEVICE_TABLE ( pci , pch_ieee1588_pcidev_id ) ;
2012-03-08 02:16:26 +04:00
2012-03-10 02:47:24 +04:00
static struct pci_driver pch_driver = {
2012-03-08 02:16:26 +04:00
. name = KBUILD_MODNAME ,
. id_table = pch_ieee1588_pcidev_id ,
. probe = pch_probe ,
. remove = pch_remove ,
} ;
2022-02-08 00:07:28 +03:00
module_pci_driver ( pch_driver ) ;
2012-03-08 02:16:26 +04:00
2013-03-26 23:07:23 +04:00
module_param_string ( station ,
pch_param . station , sizeof ( pch_param . station ) , 0444 ) ;
2012-03-08 02:16:26 +04:00
MODULE_PARM_DESC ( station ,
2013-03-26 07:54:16 +04:00
" IEEE 1588 station address to use - colon separated hex values " ) ;
2012-03-08 02:16:26 +04:00
MODULE_AUTHOR ( " LAPIS SEMICONDUCTOR, <tshimizu818@gmail.com> " ) ;
MODULE_DESCRIPTION ( " PTP clock using the EG20T timer " ) ;
MODULE_LICENSE ( " GPL " ) ;