2019-10-31 23:20:07 -04:00
// SPDX-License-Identifier: GPL-2.0+
/*
* PTP hardware clock driver for the IDT ClockMatrix ( TM ) family of timing and
* synchronization devices .
*
* Copyright ( C ) 2019 Integrated Device Technology , Inc . , a Renesas Company .
*/
# include <linux/firmware.h>
2021-09-24 15:01:32 -04:00
# include <linux/platform_device.h>
2019-10-31 23:20:07 -04:00
# include <linux/module.h>
# include <linux/ptp_clock_kernel.h>
# include <linux/delay.h>
2020-05-01 23:35:38 -04:00
# include <linux/jiffies.h>
2019-10-31 23:20:07 -04:00
# include <linux/kernel.h>
# include <linux/timekeeping.h>
2020-07-28 16:00:30 -04:00
# include <linux/string.h>
2021-09-24 15:01:32 -04:00
# include <linux/of.h>
# include <linux/mfd/rsmu.h>
# include <linux/mfd/idt8a340_reg.h>
# include <asm/unaligned.h>
2019-10-31 23:20:07 -04:00
# include "ptp_private.h"
# include "ptp_clockmatrix.h"
MODULE_DESCRIPTION ( " Driver for IDT ClockMatrix(TM) family " ) ;
MODULE_AUTHOR ( " Richard Cochran <richardcochran@gmail.com> " ) ;
MODULE_AUTHOR ( " IDT support-1588 <IDT-support-1588@lm.renesas.com> " ) ;
MODULE_VERSION ( " 1.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;
2020-07-28 16:00:30 -04:00
/*
* The name of the firmware file to be loaded
* over - rides any automatic selection
*/
static char * firmware ;
module_param ( firmware , charp , 0 ) ;
2019-10-31 23:20:07 -04:00
# define SETTIME_CORRECTION (0)
2021-09-24 15:01:32 -04:00
# define EXTTS_PERIOD_MS (95)
2019-10-31 23:20:07 -04:00
2021-09-13 16:12:34 -04:00
static int _idtcm_adjfine ( struct idtcm_channel * channel , long scaled_ppm ) ;
2021-09-24 15:01:32 -04:00
static inline int idtcm_read ( struct idtcm * idtcm ,
u16 module ,
u16 regaddr ,
u8 * buf ,
u16 count )
{
return regmap_bulk_read ( idtcm - > regmap , module + regaddr , buf , count ) ;
}
static inline int idtcm_write ( struct idtcm * idtcm ,
u16 module ,
u16 regaddr ,
u8 * buf ,
u16 count )
{
return regmap_bulk_write ( idtcm - > regmap , module + regaddr , buf , count ) ;
}
2021-09-13 16:12:33 -04:00
static int contains_full_configuration ( struct idtcm * idtcm ,
const struct firmware * fw )
2020-12-08 10:41:54 -05:00
{
struct idtcm_fwrc * rec = ( struct idtcm_fwrc * ) fw - > data ;
2021-09-13 16:12:33 -04:00
u16 scratch = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , SCRATCH ) ;
s32 full_count ;
2020-12-08 10:41:54 -05:00
s32 count = 0 ;
u16 regaddr ;
u8 loaddr ;
s32 len ;
2021-09-13 16:12:33 -04:00
/* 4 bytes skipped every 0x80 */
full_count = ( scratch - GPIO_USER_CONTROL ) -
( ( scratch > > 7 ) - ( GPIO_USER_CONTROL > > 7 ) ) * 4 ;
2020-12-08 10:41:54 -05:00
/* If the firmware contains 'full configuration' SM_RESET can be used
* to ensure proper configuration .
*
* Full configuration is defined as the number of programmable
* bytes within the configuration range minus page offset addr range .
*/
for ( len = fw - > size ; len > 0 ; len - = sizeof ( * rec ) ) {
regaddr = rec - > hiaddr < < 8 ;
regaddr | = rec - > loaddr ;
loaddr = rec - > loaddr ;
rec + + ;
/* Top (status registers) and bottom are read-only */
2021-09-13 16:12:33 -04:00
if ( regaddr < GPIO_USER_CONTROL | | regaddr > = scratch )
2020-12-08 10:41:54 -05:00
continue ;
/* Page size 128, last 4 bytes of page skipped */
if ( ( loaddr > 0x7b & & loaddr < = 0x7f ) | | loaddr > 0xfb )
continue ;
count + + ;
}
return ( count > = full_count ) ;
}
2019-10-31 23:20:07 -04:00
static int char_array_to_timespec ( u8 * buf ,
u8 count ,
struct timespec64 * ts )
{
u8 i ;
u64 nsec ;
time64_t sec ;
if ( count < TOD_BYTE_COUNT )
return 1 ;
/* Sub-nanoseconds are in buf[0]. */
nsec = buf [ 4 ] ;
for ( i = 0 ; i < 3 ; i + + ) {
nsec < < = 8 ;
nsec | = buf [ 3 - i ] ;
}
sec = buf [ 10 ] ;
for ( i = 0 ; i < 5 ; i + + ) {
sec < < = 8 ;
sec | = buf [ 9 - i ] ;
}
ts - > tv_sec = sec ;
ts - > tv_nsec = nsec ;
return 0 ;
}
static int timespec_to_char_array ( struct timespec64 const * ts ,
u8 * buf ,
u8 count )
{
u8 i ;
s32 nsec ;
time64_t sec ;
if ( count < TOD_BYTE_COUNT )
return 1 ;
nsec = ts - > tv_nsec ;
sec = ts - > tv_sec ;
/* Sub-nanoseconds are in buf[0]. */
buf [ 0 ] = 0 ;
for ( i = 1 ; i < 5 ; i + + ) {
buf [ i ] = nsec & 0xff ;
nsec > > = 8 ;
}
for ( i = 5 ; i < TOD_BYTE_COUNT ; i + + ) {
buf [ i ] = sec & 0xff ;
sec > > = 8 ;
}
return 0 ;
}
2020-11-24 21:58:35 -05:00
static int idtcm_strverscmp ( const char * version1 , const char * version2 )
2020-07-28 16:00:30 -04:00
{
2020-11-24 21:58:35 -05:00
u8 ver1 [ 3 ] , ver2 [ 3 ] ;
int i ;
2020-07-28 16:00:30 -04:00
2020-11-24 21:58:35 -05:00
if ( sscanf ( version1 , " %hhu.%hhu.%hhu " ,
& ver1 [ 0 ] , & ver1 [ 1 ] , & ver1 [ 2 ] ) ! = 3 )
return - 1 ;
if ( sscanf ( version2 , " %hhu.%hhu.%hhu " ,
& ver2 [ 0 ] , & ver2 [ 1 ] , & ver2 [ 2 ] ) ! = 3 )
return - 1 ;
2020-07-28 16:00:30 -04:00
2020-11-24 21:58:35 -05:00
for ( i = 0 ; i < 3 ; i + + ) {
if ( ver1 [ i ] > ver2 [ i ] )
return 1 ;
if ( ver1 [ i ] < ver2 [ i ] )
return - 1 ;
2020-07-28 16:00:30 -04:00
}
2020-11-24 21:58:35 -05:00
return 0 ;
2020-07-28 16:00:30 -04:00
}
2021-09-13 16:12:33 -04:00
static enum fw_version idtcm_fw_version ( const char * version )
{
enum fw_version ver = V_DEFAULT ;
if ( idtcm_strverscmp ( version , " 4.8.7 " ) > = 0 )
ver = V487 ;
if ( idtcm_strverscmp ( version , " 5.2.0 " ) > = 0 )
ver = V520 ;
return ver ;
}
2020-12-08 10:41:54 -05:00
static int clear_boot_status ( struct idtcm * idtcm )
{
u8 buf [ 4 ] = { 0 } ;
2021-02-17 00:42:17 -05:00
return idtcm_write ( idtcm , GENERAL_STATUS , BOOT_STATUS , buf , sizeof ( buf ) ) ;
2020-12-08 10:41:54 -05:00
}
static int read_boot_status ( struct idtcm * idtcm , u32 * status )
{
int err ;
u8 buf [ 4 ] = { 0 } ;
err = idtcm_read ( idtcm , GENERAL_STATUS , BOOT_STATUS , buf , sizeof ( buf ) ) ;
* status = ( buf [ 3 ] < < 24 ) | ( buf [ 2 ] < < 16 ) | ( buf [ 1 ] < < 8 ) | buf [ 0 ] ;
return err ;
}
static int wait_for_boot_status_ready ( struct idtcm * idtcm )
{
u32 status = 0 ;
u8 i = 30 ; /* 30 * 100ms = 3s */
int err ;
do {
err = read_boot_status ( idtcm , & status ) ;
if ( err )
return err ;
if ( status = = 0xA0 )
return 0 ;
msleep ( 100 ) ;
i - - ;
} while ( i ) ;
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev , " %s timed out " , __func__ ) ;
2020-12-08 10:41:54 -05:00
return - EBUSY ;
}
2022-05-16 10:47:06 -04:00
static int arm_tod_read_trig_sel_refclk ( struct idtcm_channel * channel , u8 ref )
2021-09-24 15:01:32 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
2022-05-16 10:47:06 -04:00
u16 tod_read_cmd = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , TOD_READ_SECONDARY_CMD ) ;
u8 val = 0 ;
2021-09-24 15:01:32 -04:00
int err ;
2022-05-16 10:47:06 -04:00
val & = ~ ( WR_REF_INDEX_MASK < < WR_REF_INDEX_SHIFT ) ;
val | = ( ref < < WR_REF_INDEX_SHIFT ) ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
err = idtcm_write ( idtcm , channel - > tod_read_secondary ,
TOD_READ_SECONDARY_SEL_CFG_0 , & val , sizeof ( val ) ) ;
2021-09-24 15:01:32 -04:00
if ( err )
return err ;
2022-05-16 10:47:06 -04:00
val = 0 | ( SCSR_TOD_READ_TRIG_SEL_REFCLK < < TOD_READ_TRIGGER_SHIFT ) ;
err = idtcm_write ( idtcm , channel - > tod_read_secondary , tod_read_cmd ,
& val , sizeof ( val ) ) ;
if ( err )
dev_err ( idtcm - > dev , " %s: err = %d " , __func__ , err ) ;
2021-09-24 15:01:32 -04:00
return err ;
}
2022-05-16 10:47:06 -04:00
static bool is_single_shot ( u8 mask )
2021-09-24 15:01:32 -04:00
{
2022-05-16 10:47:06 -04:00
/* Treat single bit ToD masks as continuous trigger */
2022-05-24 10:45:01 -04:00
return ! ( mask < = 8 & & is_power_of_2 ( mask ) ) ;
2022-05-16 10:47:06 -04:00
}
static int idtcm_extts_enable ( struct idtcm_channel * channel ,
struct ptp_clock_request * rq , int on )
{
u8 index = rq - > extts . index ;
struct idtcm * idtcm ;
u8 mask = 1 < < index ;
2021-09-24 15:01:32 -04:00
int err = 0 ;
2022-05-16 10:47:06 -04:00
u8 old_mask ;
int ref ;
idtcm = channel - > idtcm ;
old_mask = idtcm - > extts_mask ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
/* Reject requests with unsupported flags */
if ( rq - > extts . flags & ~ ( PTP_ENABLE_FEATURE |
PTP_RISING_EDGE |
PTP_FALLING_EDGE |
PTP_STRICT_FLAGS ) )
return - EOPNOTSUPP ;
/* Reject requests to enable time stamping on falling edge */
if ( ( rq - > extts . flags & PTP_ENABLE_FEATURE ) & &
( rq - > extts . flags & PTP_FALLING_EDGE ) )
return - EOPNOTSUPP ;
if ( index > = MAX_TOD )
2021-09-24 15:01:32 -04:00
return - EINVAL ;
2022-05-16 10:47:06 -04:00
if ( on ) {
/* Support triggering more than one TOD_0/1/2/3 by same pin */
/* Use the pin configured for the channel */
ref = ptp_find_pin ( channel - > ptp_clock , PTP_PF_EXTTS , channel - > tod ) ;
if ( ref < 0 ) {
dev_err ( idtcm - > dev , " %s: No valid pin found for TOD%d! \n " ,
__func__ , channel - > tod ) ;
return - EBUSY ;
}
err = arm_tod_read_trig_sel_refclk ( & idtcm - > channel [ index ] , ref ) ;
2021-09-24 15:01:32 -04:00
if ( err = = 0 ) {
idtcm - > extts_mask | = mask ;
2022-05-16 10:47:06 -04:00
idtcm - > event_channel [ index ] = channel ;
idtcm - > channel [ index ] . refn = ref ;
idtcm - > extts_single_shot = is_single_shot ( idtcm - > extts_mask ) ;
if ( old_mask )
return 0 ;
schedule_delayed_work ( & idtcm - > extts_work ,
msecs_to_jiffies ( EXTTS_PERIOD_MS ) ) ;
2021-09-24 15:01:32 -04:00
}
2022-05-16 10:47:06 -04:00
} else {
2021-09-24 15:01:32 -04:00
idtcm - > extts_mask & = ~ mask ;
2022-05-16 10:47:06 -04:00
idtcm - > extts_single_shot = is_single_shot ( idtcm - > extts_mask ) ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
if ( idtcm - > extts_mask = = 0 )
cancel_delayed_work ( & idtcm - > extts_work ) ;
}
2021-09-24 15:01:32 -04:00
return err ;
}
2021-02-17 00:42:12 -05:00
static int read_sys_apll_status ( struct idtcm * idtcm , u8 * status )
{
return idtcm_read ( idtcm , STATUS , DPLL_SYS_APLL_STATUS , status ,
sizeof ( u8 ) ) ;
}
static int read_sys_dpll_status ( struct idtcm * idtcm , u8 * status )
{
return idtcm_read ( idtcm , STATUS , DPLL_SYS_STATUS , status , sizeof ( u8 ) ) ;
}
static int wait_for_sys_apll_dpll_lock ( struct idtcm * idtcm )
{
unsigned long timeout = jiffies + msecs_to_jiffies ( LOCK_TIMEOUT_MS ) ;
u8 apll = 0 ;
u8 dpll = 0 ;
int err ;
do {
err = read_sys_apll_status ( idtcm , & apll ) ;
if ( err )
return err ;
err = read_sys_dpll_status ( idtcm , & dpll ) ;
if ( err )
return err ;
apll & = SYS_APLL_LOSS_LOCK_LIVE_MASK ;
dpll & = DPLL_SYS_STATE_MASK ;
2021-09-13 16:12:33 -04:00
if ( apll = = SYS_APLL_LOSS_LOCK_LIVE_LOCKED & &
dpll = = DPLL_STATE_LOCKED ) {
2021-02-17 00:42:12 -05:00
return 0 ;
} else if ( dpll = = DPLL_STATE_FREERUN | |
dpll = = DPLL_STATE_HOLDOVER | |
dpll = = DPLL_STATE_OPEN_LOOP ) {
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev ,
2021-02-17 00:42:12 -05:00
" No wait state: DPLL_SYS_STATE %d " , dpll ) ;
return - EPERM ;
}
msleep ( LOCK_POLL_INTERVAL_MS ) ;
} while ( time_is_after_jiffies ( timeout ) ) ;
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev ,
2021-02-17 00:42:12 -05:00
" %d ms lock timeout: SYS APLL Loss Lock %d SYS DPLL state %d " ,
LOCK_TIMEOUT_MS , apll , dpll ) ;
return - ETIME ;
}
static void wait_for_chip_ready ( struct idtcm * idtcm )
{
if ( wait_for_boot_status_ready ( idtcm ) )
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev , " BOOT_STATUS != 0xA0 " ) ;
2021-02-17 00:42:12 -05:00
if ( wait_for_sys_apll_dpll_lock ( idtcm ) )
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev ,
2021-02-17 00:42:12 -05:00
" Continuing while SYS APLL/DPLL is not locked " ) ;
}
2022-05-16 10:47:06 -04:00
static int _idtcm_gettime_triggered ( struct idtcm_channel * channel ,
struct timespec64 * ts )
{
struct idtcm * idtcm = channel - > idtcm ;
u16 tod_read_cmd = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , TOD_READ_SECONDARY_CMD ) ;
u8 buf [ TOD_BYTE_COUNT ] ;
u8 trigger ;
int err ;
err = idtcm_read ( idtcm , channel - > tod_read_secondary ,
tod_read_cmd , & trigger , sizeof ( trigger ) ) ;
if ( err )
return err ;
if ( trigger & TOD_READ_TRIGGER_MASK )
return - EBUSY ;
err = idtcm_read ( idtcm , channel - > tod_read_secondary ,
TOD_READ_SECONDARY_BASE , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
return char_array_to_timespec ( buf , sizeof ( buf ) , ts ) ;
}
2019-10-31 23:20:07 -04:00
static int _idtcm_gettime ( struct idtcm_channel * channel ,
2021-09-24 15:01:32 -04:00
struct timespec64 * ts , u8 timeout )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
2021-09-13 16:12:33 -04:00
u16 tod_read_cmd = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , TOD_READ_PRIMARY_CMD ) ;
2019-10-31 23:20:07 -04:00
u8 buf [ TOD_BYTE_COUNT ] ;
u8 trigger ;
int err ;
2020-07-28 16:00:30 -04:00
/* wait trigger to be 0 */
2021-09-24 15:01:32 -04:00
do {
if ( timeout - - = = 0 )
return - EIO ;
2020-07-28 16:00:30 -04:00
if ( idtcm - > calculate_overhead_flag )
idtcm - > start_time = ktime_get_raw ( ) ;
err = idtcm_read ( idtcm , channel - > tod_read_primary ,
2021-09-13 16:12:33 -04:00
tod_read_cmd , & trigger ,
2020-07-28 16:00:30 -04:00
sizeof ( trigger ) ) ;
if ( err )
return err ;
2021-09-24 15:01:32 -04:00
} while ( trigger & TOD_READ_TRIGGER_MASK ) ;
2019-10-31 23:20:07 -04:00
err = idtcm_read ( idtcm , channel - > tod_read_primary ,
2022-05-16 10:47:06 -04:00
TOD_READ_PRIMARY_BASE , buf , sizeof ( buf ) ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
err = char_array_to_timespec ( buf , sizeof ( buf ) , ts ) ;
return err ;
}
2021-09-24 15:01:32 -04:00
static int idtcm_extts_check_channel ( struct idtcm * idtcm , u8 todn )
{
struct idtcm_channel * ptp_channel , * extts_channel ;
struct ptp_clock_event event ;
struct timespec64 ts ;
u32 dco_delay = 0 ;
int err ;
extts_channel = & idtcm - > channel [ todn ] ;
ptp_channel = idtcm - > event_channel [ todn ] ;
2022-05-16 10:47:06 -04:00
2021-09-24 15:01:32 -04:00
if ( extts_channel = = ptp_channel )
dco_delay = ptp_channel - > dco_delay ;
2022-05-16 10:47:06 -04:00
err = _idtcm_gettime_triggered ( extts_channel , & ts ) ;
if ( err )
return err ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
/* Triggered - save timestamp */
event . type = PTP_CLOCK_EXTTS ;
event . index = todn ;
event . timestamp = timespec64_to_ns ( & ts ) - dco_delay ;
ptp_clock_event ( ptp_channel - > ptp_clock , & event ) ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
return err ;
2021-09-24 15:01:32 -04:00
}
static int _idtcm_gettime_immediate ( struct idtcm_channel * channel ,
struct timespec64 * ts )
{
struct idtcm * idtcm = channel - > idtcm ;
2022-05-16 10:47:06 -04:00
u16 tod_read_cmd = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , TOD_READ_PRIMARY_CMD ) ;
u8 val = ( SCSR_TOD_READ_TRIG_SEL_IMMEDIATE < < TOD_READ_TRIGGER_SHIFT ) ;
int err ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
err = idtcm_write ( idtcm , channel - > tod_read_primary ,
tod_read_cmd , & val , sizeof ( val ) ) ;
if ( err )
return err ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
return _idtcm_gettime ( channel , ts , 10 ) ;
2021-09-24 15:01:32 -04:00
}
2019-10-31 23:20:07 -04:00
static int _sync_pll_output ( struct idtcm * idtcm ,
u8 pll ,
u8 sync_src ,
u8 qn ,
u8 qn_plus_1 )
{
int err ;
u8 val ;
u16 sync_ctrl0 ;
u16 sync_ctrl1 ;
2020-07-28 16:00:30 -04:00
u8 temp ;
2019-10-31 23:20:07 -04:00
2021-02-17 00:42:18 -05:00
if ( qn = = 0 & & qn_plus_1 = = 0 )
2019-10-31 23:20:07 -04:00
return 0 ;
switch ( pll ) {
case 0 :
sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1 ;
break ;
case 1 :
sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1 ;
break ;
case 2 :
sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1 ;
break ;
case 3 :
sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1 ;
break ;
case 4 :
sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1 ;
break ;
case 5 :
sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1 ;
break ;
case 6 :
sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1 ;
break ;
case 7 :
sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0 ;
sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1 ;
break ;
default :
return - EINVAL ;
}
val = SYNCTRL1_MASTER_SYNC_RST ;
/* Place master sync in reset */
err = idtcm_write ( idtcm , 0 , sync_ctrl1 , & val , sizeof ( val ) ) ;
if ( err )
return err ;
err = idtcm_write ( idtcm , 0 , sync_ctrl0 , & sync_src , sizeof ( sync_src ) ) ;
if ( err )
return err ;
/* Set sync trigger mask */
val | = SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG ;
if ( qn )
val | = SYNCTRL1_Q0_DIV_SYNC_TRIG ;
if ( qn_plus_1 )
val | = SYNCTRL1_Q1_DIV_SYNC_TRIG ;
err = idtcm_write ( idtcm , 0 , sync_ctrl1 , & val , sizeof ( val ) ) ;
if ( err )
return err ;
2020-07-28 16:00:30 -04:00
/* PLL5 can have OUT8 as second additional output. */
2021-02-17 00:42:18 -05:00
if ( pll = = 5 & & qn_plus_1 ! = 0 ) {
2020-07-28 16:00:30 -04:00
err = idtcm_read ( idtcm , 0 , HW_Q8_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
temp & = ~ ( Q9_TO_Q8_SYNC_TRIG ) ;
err = idtcm_write ( idtcm , 0 , HW_Q8_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
temp | = Q9_TO_Q8_SYNC_TRIG ;
err = idtcm_write ( idtcm , 0 , HW_Q8_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
}
/* PLL6 can have OUT11 as second additional output. */
2021-02-17 00:42:18 -05:00
if ( pll = = 6 & & qn_plus_1 ! = 0 ) {
2020-07-28 16:00:30 -04:00
err = idtcm_read ( idtcm , 0 , HW_Q11_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
temp & = ~ ( Q10_TO_Q11_SYNC_TRIG ) ;
err = idtcm_write ( idtcm , 0 , HW_Q11_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
temp | = Q10_TO_Q11_SYNC_TRIG ;
err = idtcm_write ( idtcm , 0 , HW_Q11_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
}
2019-10-31 23:20:07 -04:00
/* Place master sync out of reset */
val & = ~ ( SYNCTRL1_MASTER_SYNC_RST ) ;
err = idtcm_write ( idtcm , 0 , sync_ctrl1 , & val , sizeof ( val ) ) ;
return err ;
}
static int idtcm_sync_pps_output ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
u8 pll ;
u8 qn ;
u8 qn_plus_1 ;
int err = 0 ;
2020-07-28 16:00:30 -04:00
u8 out8_mux = 0 ;
u8 out11_mux = 0 ;
u8 temp ;
2019-10-31 23:20:07 -04:00
u16 output_mask = channel - > output_mask ;
2020-07-28 16:00:30 -04:00
err = idtcm_read ( idtcm , 0 , HW_Q8_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
if ( ( temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK ) = =
Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK )
out8_mux = 1 ;
err = idtcm_read ( idtcm , 0 , HW_Q11_CTRL_SPARE ,
& temp , sizeof ( temp ) ) ;
if ( err )
return err ;
if ( ( temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK ) = =
Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK )
out11_mux = 1 ;
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
for ( pll = 0 ; pll < 8 ; pll + + ) {
qn = 0 ;
qn_plus_1 = 0 ;
2019-10-31 23:20:07 -04:00
if ( pll < 4 ) {
/* First 4 pll has 2 outputs */
2020-07-28 16:00:30 -04:00
qn = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
2019-10-31 23:20:07 -04:00
qn_plus_1 = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
2020-07-28 16:00:30 -04:00
} else if ( pll = = 4 ) {
if ( out8_mux = = 0 ) {
qn = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
}
} else if ( pll = = 5 ) {
if ( out8_mux ) {
qn_plus_1 = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
}
qn = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
} else if ( pll = = 6 ) {
qn = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
if ( out11_mux ) {
qn_plus_1 = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
}
} else if ( pll = = 7 ) {
if ( out11_mux = = 0 ) {
qn = output_mask & 0x1 ;
output_mask = output_mask > > 1 ;
}
2019-10-31 23:20:07 -04:00
}
2021-02-17 00:42:18 -05:00
if ( qn ! = 0 | | qn_plus_1 ! = 0 )
2021-09-13 16:12:33 -04:00
err = _sync_pll_output ( idtcm , pll , channel - > sync_src ,
qn , qn_plus_1 ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
}
return err ;
}
2020-07-28 16:00:30 -04:00
static int _idtcm_set_dpll_hw_tod ( struct idtcm_channel * channel ,
2021-09-13 16:12:34 -04:00
struct timespec64 const * ts ,
enum hw_tod_write_trig_sel wr_trig )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
u8 buf [ TOD_BYTE_COUNT ] ;
u8 cmd ;
int err ;
struct timespec64 local_ts = * ts ;
s64 total_overhead_ns ;
/* Configure HW TOD write trigger. */
err = idtcm_read ( idtcm , channel - > hw_dpll_n , HW_DPLL_TOD_CTRL_1 ,
& cmd , sizeof ( cmd ) ) ;
if ( err )
return err ;
cmd & = ~ ( 0x0f ) ;
cmd | = wr_trig | 0x08 ;
err = idtcm_write ( idtcm , channel - > hw_dpll_n , HW_DPLL_TOD_CTRL_1 ,
& cmd , sizeof ( cmd ) ) ;
if ( err )
return err ;
if ( wr_trig ! = HW_TOD_WR_TRIG_SEL_MSB ) {
err = timespec_to_char_array ( & local_ts , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
err = idtcm_write ( idtcm , channel - > hw_dpll_n ,
HW_DPLL_TOD_OVR__0 , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
}
/* ARM HW TOD write trigger. */
cmd & = ~ ( 0x08 ) ;
err = idtcm_write ( idtcm , channel - > hw_dpll_n , HW_DPLL_TOD_CTRL_1 ,
& cmd , sizeof ( cmd ) ) ;
if ( wr_trig = = HW_TOD_WR_TRIG_SEL_MSB ) {
if ( idtcm - > calculate_overhead_flag ) {
2020-01-07 09:47:57 -05:00
/* Assumption: I2C @ 400KHz */
2020-12-08 10:41:56 -05:00
ktime_t diff = ktime_sub ( ktime_get_raw ( ) ,
idtcm - > start_time ) ;
total_overhead_ns = ktime_to_ns ( diff )
2019-10-31 23:20:07 -04:00
+ idtcm - > tod_write_overhead_ns
+ SETTIME_CORRECTION ;
timespec64_add_ns ( & local_ts , total_overhead_ns ) ;
idtcm - > calculate_overhead_flag = 0 ;
}
err = timespec_to_char_array ( & local_ts , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
err = idtcm_write ( idtcm , channel - > hw_dpll_n ,
HW_DPLL_TOD_OVR__0 , buf , sizeof ( buf ) ) ;
}
return err ;
}
2020-07-28 16:00:30 -04:00
static int _idtcm_set_dpll_scsr_tod ( struct idtcm_channel * channel ,
struct timespec64 const * ts ,
enum scsr_tod_write_trig_sel wr_trig ,
enum scsr_tod_write_type_sel wr_type )
{
struct idtcm * idtcm = channel - > idtcm ;
unsigned char buf [ TOD_BYTE_COUNT ] , cmd ;
struct timespec64 local_ts = * ts ;
int err , count = 0 ;
timespec64_add_ns ( & local_ts , SETTIME_CORRECTION ) ;
err = timespec_to_char_array ( & local_ts , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
err = idtcm_write ( idtcm , channel - > tod_write , TOD_WRITE ,
buf , sizeof ( buf ) ) ;
if ( err )
return err ;
/* Trigger the write operation. */
err = idtcm_read ( idtcm , channel - > tod_write , TOD_WRITE_CMD ,
& cmd , sizeof ( cmd ) ) ;
if ( err )
return err ;
cmd & = ~ ( TOD_WRITE_SELECTION_MASK < < TOD_WRITE_SELECTION_SHIFT ) ;
cmd & = ~ ( TOD_WRITE_TYPE_MASK < < TOD_WRITE_TYPE_SHIFT ) ;
cmd | = ( wr_trig < < TOD_WRITE_SELECTION_SHIFT ) ;
cmd | = ( wr_type < < TOD_WRITE_TYPE_SHIFT ) ;
err = idtcm_write ( idtcm , channel - > tod_write , TOD_WRITE_CMD ,
& cmd , sizeof ( cmd ) ) ;
if ( err )
return err ;
/* Wait for the operation to complete. */
while ( 1 ) {
/* pps trigger takes up to 1 sec to complete */
if ( wr_trig = = SCSR_TOD_WR_TRIG_SEL_TODPPS )
msleep ( 50 ) ;
err = idtcm_read ( idtcm , channel - > tod_write , TOD_WRITE_CMD ,
& cmd , sizeof ( cmd ) ) ;
if ( err )
return err ;
2020-12-08 10:41:54 -05:00
if ( ( cmd & TOD_WRITE_SELECTION_MASK ) = = 0 )
2020-07-28 16:00:30 -04:00
break ;
if ( + + count > 20 ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Timed out waiting for the write counter " ) ;
2020-07-28 16:00:30 -04:00
return - EIO ;
}
}
return 0 ;
}
2021-09-13 16:12:33 -04:00
static int get_output_base_addr ( enum fw_version ver , u8 outn )
2020-12-08 10:41:56 -05:00
{
int base ;
switch ( outn ) {
case 0 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_0 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 1 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_1 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 2 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_2 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 3 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_3 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 4 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_4 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 5 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_5 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 6 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_6 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 7 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_7 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 8 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_8 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 9 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_9 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 10 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_10 ) ;
2020-12-08 10:41:56 -05:00
break ;
case 11 :
2021-09-13 16:12:33 -04:00
base = IDTCM_FW_REG ( ver , V520 , OUTPUT_11 ) ;
2020-12-08 10:41:56 -05:00
break ;
default :
base = - EINVAL ;
}
return base ;
}
2020-12-08 10:41:57 -05:00
static int _idtcm_settime_deprecated ( struct idtcm_channel * channel ,
struct timespec64 const * ts )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2020-12-08 10:41:54 -05:00
err = _idtcm_set_dpll_hw_tod ( channel , ts , HW_TOD_WR_TRIG_SEL_MSB ) ;
2020-07-28 16:00:30 -04:00
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" %s: Set HW ToD failed " , __func__ ) ;
2019-10-31 23:20:07 -04:00
return err ;
2020-07-28 16:00:30 -04:00
}
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
return idtcm_sync_pps_output ( channel ) ;
}
2019-10-31 23:20:07 -04:00
2020-12-08 10:41:57 -05:00
static int _idtcm_settime ( struct idtcm_channel * channel ,
struct timespec64 const * ts ,
enum scsr_tod_write_type_sel wr_type )
2020-07-28 16:00:30 -04:00
{
return _idtcm_set_dpll_scsr_tod ( channel , ts ,
SCSR_TOD_WR_TRIG_SEL_IMMEDIATE ,
wr_type ) ;
2019-10-31 23:20:07 -04:00
}
static int idtcm_set_phase_pull_in_offset ( struct idtcm_channel * channel ,
s32 offset_ns )
{
int err ;
int i ;
struct idtcm * idtcm = channel - > idtcm ;
u8 buf [ 4 ] ;
for ( i = 0 ; i < 4 ; i + + ) {
buf [ i ] = 0xff & ( offset_ns ) ;
offset_ns > > = 8 ;
}
err = idtcm_write ( idtcm , channel - > dpll_phase_pull_in , PULL_IN_OFFSET ,
buf , sizeof ( buf ) ) ;
return err ;
}
static int idtcm_set_phase_pull_in_slope_limit ( struct idtcm_channel * channel ,
u32 max_ffo_ppb )
{
int err ;
u8 i ;
struct idtcm * idtcm = channel - > idtcm ;
u8 buf [ 3 ] ;
if ( max_ffo_ppb & 0xff000000 )
max_ffo_ppb = 0 ;
for ( i = 0 ; i < 3 ; i + + ) {
buf [ i ] = 0xff & ( max_ffo_ppb ) ;
max_ffo_ppb > > = 8 ;
}
err = idtcm_write ( idtcm , channel - > dpll_phase_pull_in ,
PULL_IN_SLOPE_LIMIT , buf , sizeof ( buf ) ) ;
return err ;
}
static int idtcm_start_phase_pull_in ( struct idtcm_channel * channel )
{
int err ;
struct idtcm * idtcm = channel - > idtcm ;
u8 buf ;
err = idtcm_read ( idtcm , channel - > dpll_phase_pull_in , PULL_IN_CTRL ,
& buf , sizeof ( buf ) ) ;
if ( err )
return err ;
if ( buf = = 0 ) {
buf = 0x01 ;
err = idtcm_write ( idtcm , channel - > dpll_phase_pull_in ,
PULL_IN_CTRL , & buf , sizeof ( buf ) ) ;
} else {
err = - EBUSY ;
}
return err ;
}
2021-09-13 16:12:34 -04:00
static int do_phase_pull_in_fw ( struct idtcm_channel * channel ,
s32 offset_ns ,
u32 max_ffo_ppb )
2019-10-31 23:20:07 -04:00
{
int err ;
err = idtcm_set_phase_pull_in_offset ( channel , - offset_ns ) ;
if ( err )
return err ;
err = idtcm_set_phase_pull_in_slope_limit ( channel , max_ffo_ppb ) ;
if ( err )
return err ;
err = idtcm_start_phase_pull_in ( channel ) ;
return err ;
}
2020-07-28 16:00:30 -04:00
static int set_tod_write_overhead ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
s64 current_ns = 0 ;
s64 lowest_ns = 0 ;
int err ;
u8 i ;
ktime_t start ;
ktime_t stop ;
2020-12-08 10:41:56 -05:00
ktime_t diff ;
2020-07-28 16:00:30 -04:00
char buf [ TOD_BYTE_COUNT ] = { 0 } ;
/* Set page offset */
idtcm_write ( idtcm , channel - > hw_dpll_n , HW_DPLL_TOD_OVR__0 ,
buf , sizeof ( buf ) ) ;
for ( i = 0 ; i < TOD_WRITE_OVERHEAD_COUNT_MAX ; i + + ) {
start = ktime_get_raw ( ) ;
err = idtcm_write ( idtcm , channel - > hw_dpll_n ,
HW_DPLL_TOD_OVR__0 , buf , sizeof ( buf ) ) ;
if ( err )
return err ;
stop = ktime_get_raw ( ) ;
2020-12-08 10:41:56 -05:00
diff = ktime_sub ( stop , start ) ;
current_ns = ktime_to_ns ( diff ) ;
2020-07-28 16:00:30 -04:00
if ( i = = 0 ) {
lowest_ns = current_ns ;
} else {
if ( current_ns < lowest_ns )
lowest_ns = current_ns ;
}
}
idtcm - > tod_write_overhead_ns = lowest_ns ;
return err ;
}
2020-12-08 10:41:57 -05:00
static int _idtcm_adjtime_deprecated ( struct idtcm_channel * channel , s64 delta )
2019-10-31 23:20:07 -04:00
{
int err ;
struct idtcm * idtcm = channel - > idtcm ;
struct timespec64 ts ;
s64 now ;
2020-12-08 10:41:57 -05:00
if ( abs ( delta ) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED ) {
2021-09-13 16:12:34 -04:00
err = channel - > do_phase_pull_in ( channel , delta , 0 ) ;
2019-10-31 23:20:07 -04:00
} else {
idtcm - > calculate_overhead_flag = 1 ;
2020-07-28 16:00:30 -04:00
err = set_tod_write_overhead ( channel ) ;
if ( err )
return err ;
2021-09-24 15:01:32 -04:00
err = _idtcm_gettime_immediate ( channel , & ts ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
now = timespec64_to_ns ( & ts ) ;
now + = delta ;
ts = ns_to_timespec64 ( now ) ;
2020-12-08 10:41:57 -05:00
err = _idtcm_settime_deprecated ( channel , & ts ) ;
2019-10-31 23:20:07 -04:00
}
return err ;
}
static int idtcm_state_machine_reset ( struct idtcm * idtcm )
{
u8 byte = SM_RESET_CMD ;
2020-12-08 10:41:54 -05:00
u32 status = 0 ;
int err ;
u8 i ;
clear_boot_status ( idtcm ) ;
2019-10-31 23:20:07 -04:00
2021-09-13 16:12:33 -04:00
err = idtcm_write ( idtcm , RESET_CTRL ,
IDTCM_FW_REG ( idtcm - > fw_ver , V520 , SM_RESET ) ,
& byte , sizeof ( byte ) ) ;
2019-10-31 23:20:07 -04:00
2020-12-08 10:41:54 -05:00
if ( ! err ) {
for ( i = 0 ; i < 30 ; i + + ) {
msleep_interruptible ( 100 ) ;
read_boot_status ( idtcm , & status ) ;
if ( status = = 0xA0 ) {
2021-09-24 15:01:32 -04:00
dev_dbg ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" SM_RESET completed in %d ms " , i * 100 ) ;
2020-12-08 10:41:54 -05:00
break ;
}
}
if ( ! status )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Timed out waiting for CM_RESET to complete " ) ;
2020-12-08 10:41:54 -05:00
}
2019-10-31 23:20:07 -04:00
return err ;
}
static int idtcm_read_hw_rev_id ( struct idtcm * idtcm , u8 * hw_rev_id )
{
2020-01-07 09:47:57 -05:00
return idtcm_read ( idtcm , HW_REVISION , REV_ID , hw_rev_id , sizeof ( u8 ) ) ;
2019-10-31 23:20:07 -04:00
}
static int idtcm_read_product_id ( struct idtcm * idtcm , u16 * product_id )
{
int err ;
u8 buf [ 2 ] = { 0 } ;
err = idtcm_read ( idtcm , GENERAL_STATUS , PRODUCT_ID , buf , sizeof ( buf ) ) ;
* product_id = ( buf [ 1 ] < < 8 ) | buf [ 0 ] ;
return err ;
}
static int idtcm_read_major_release ( struct idtcm * idtcm , u8 * major )
{
int err ;
u8 buf = 0 ;
err = idtcm_read ( idtcm , GENERAL_STATUS , MAJ_REL , & buf , sizeof ( buf ) ) ;
* major = buf > > 1 ;
return err ;
}
static int idtcm_read_minor_release ( struct idtcm * idtcm , u8 * minor )
{
return idtcm_read ( idtcm , GENERAL_STATUS , MIN_REL , minor , sizeof ( u8 ) ) ;
}
static int idtcm_read_hotfix_release ( struct idtcm * idtcm , u8 * hotfix )
{
return idtcm_read ( idtcm ,
GENERAL_STATUS ,
HOTFIX_REL ,
hotfix ,
sizeof ( u8 ) ) ;
}
2020-01-07 09:47:57 -05:00
static int idtcm_read_otp_scsr_config_select ( struct idtcm * idtcm ,
u8 * config_select )
2019-10-31 23:20:07 -04:00
{
2020-01-07 09:47:57 -05:00
return idtcm_read ( idtcm , GENERAL_STATUS , OTP_SCSR_CONFIG_SELECT ,
config_select , sizeof ( u8 ) ) ;
2019-10-31 23:20:07 -04:00
}
static int set_pll_output_mask ( struct idtcm * idtcm , u16 addr , u8 val )
{
int err = 0 ;
switch ( addr ) {
2020-07-28 16:00:30 -04:00
case TOD0_OUT_ALIGN_MASK_ADDR :
2019-10-31 23:20:07 -04:00
SET_U16_LSB ( idtcm - > channel [ 0 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD0_OUT_ALIGN_MASK_ADDR + 1 :
2019-10-31 23:20:07 -04:00
SET_U16_MSB ( idtcm - > channel [ 0 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD1_OUT_ALIGN_MASK_ADDR :
2019-10-31 23:20:07 -04:00
SET_U16_LSB ( idtcm - > channel [ 1 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD1_OUT_ALIGN_MASK_ADDR + 1 :
2019-10-31 23:20:07 -04:00
SET_U16_MSB ( idtcm - > channel [ 1 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD2_OUT_ALIGN_MASK_ADDR :
2019-10-31 23:20:07 -04:00
SET_U16_LSB ( idtcm - > channel [ 2 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD2_OUT_ALIGN_MASK_ADDR + 1 :
2019-10-31 23:20:07 -04:00
SET_U16_MSB ( idtcm - > channel [ 2 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD3_OUT_ALIGN_MASK_ADDR :
2019-10-31 23:20:07 -04:00
SET_U16_LSB ( idtcm - > channel [ 3 ] . output_mask , val ) ;
break ;
2020-07-28 16:00:30 -04:00
case TOD3_OUT_ALIGN_MASK_ADDR + 1 :
2019-10-31 23:20:07 -04:00
SET_U16_MSB ( idtcm - > channel [ 3 ] . output_mask , val ) ;
break ;
default :
2020-07-28 16:00:30 -04:00
err = - EFAULT ; /* Bad address */ ;
2019-10-31 23:20:07 -04:00
break ;
}
return err ;
}
2020-07-28 16:00:30 -04:00
static int set_tod_ptp_pll ( struct idtcm * idtcm , u8 index , u8 pll )
{
if ( index > = MAX_TOD ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " ToD%d not supported " , index ) ;
2020-07-28 16:00:30 -04:00
return - EINVAL ;
}
if ( pll > = MAX_PLL ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Pll%d not supported " , pll ) ;
2020-07-28 16:00:30 -04:00
return - EINVAL ;
}
idtcm - > channel [ index ] . pll = pll ;
return 0 ;
}
2019-10-31 23:20:07 -04:00
static int check_and_set_masks ( struct idtcm * idtcm ,
u16 regaddr ,
u8 val )
{
int err = 0 ;
2020-07-28 16:00:30 -04:00
switch ( regaddr ) {
case TOD_MASK_ADDR :
if ( ( val & 0xf0 ) | | ! ( val & 0x0f ) ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Invalid TOD mask 0x%02x " , val ) ;
2020-07-28 16:00:30 -04:00
err = - EINVAL ;
} else {
idtcm - > tod_mask = val ;
}
break ;
case TOD0_PTP_PLL_ADDR :
err = set_tod_ptp_pll ( idtcm , 0 , val ) ;
break ;
case TOD1_PTP_PLL_ADDR :
err = set_tod_ptp_pll ( idtcm , 1 , val ) ;
break ;
case TOD2_PTP_PLL_ADDR :
err = set_tod_ptp_pll ( idtcm , 2 , val ) ;
break ;
case TOD3_PTP_PLL_ADDR :
err = set_tod_ptp_pll ( idtcm , 3 , val ) ;
break ;
default :
err = set_pll_output_mask ( idtcm , regaddr , val ) ;
break ;
2019-10-31 23:20:07 -04:00
}
return err ;
}
2020-07-28 16:00:30 -04:00
static void display_pll_and_masks ( struct idtcm * idtcm )
2019-10-31 23:20:07 -04:00
{
u8 i ;
u8 mask ;
2021-09-24 15:01:32 -04:00
dev_dbg ( idtcm - > dev , " tod_mask = 0x%02x " , idtcm - > tod_mask ) ;
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
for ( i = 0 ; i < MAX_TOD ; i + + ) {
2019-10-31 23:20:07 -04:00
mask = 1 < < i ;
2020-07-28 16:00:30 -04:00
if ( mask & idtcm - > tod_mask )
2021-09-24 15:01:32 -04:00
dev_dbg ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" TOD%d pll = %d output_mask = 0x%04x " ,
2020-07-28 16:00:30 -04:00
i , idtcm - > channel [ i ] . pll ,
idtcm - > channel [ i ] . output_mask ) ;
2019-10-31 23:20:07 -04:00
}
}
static int idtcm_load_firmware ( struct idtcm * idtcm ,
struct device * dev )
{
2021-09-13 16:12:33 -04:00
u16 scratch = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , SCRATCH ) ;
2020-07-28 16:00:30 -04:00
char fname [ 128 ] = FW_FILENAME ;
2019-10-31 23:20:07 -04:00
const struct firmware * fw ;
struct idtcm_fwrc * rec ;
u32 regaddr ;
int err ;
s32 len ;
u8 val ;
u8 loaddr ;
2020-07-28 16:00:30 -04:00
if ( firmware ) /* module parameter */
snprintf ( fname , sizeof ( fname ) , " %s " , firmware ) ;
2019-10-31 23:20:07 -04:00
2021-09-24 15:01:32 -04:00
dev_info ( idtcm - > dev , " requesting firmware '%s' " , fname ) ;
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
err = request_firmware ( & fw , fname , dev ) ;
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2019-10-31 23:20:07 -04:00
return err ;
2020-07-28 16:00:30 -04:00
}
2019-10-31 23:20:07 -04:00
2021-09-24 15:01:32 -04:00
dev_dbg ( idtcm - > dev , " firmware size %zu bytes " , fw - > size ) ;
2019-10-31 23:20:07 -04:00
rec = ( struct idtcm_fwrc * ) fw - > data ;
2021-09-13 16:12:33 -04:00
if ( contains_full_configuration ( idtcm , fw ) )
2019-10-31 23:20:07 -04:00
idtcm_state_machine_reset ( idtcm ) ;
for ( len = fw - > size ; len > 0 ; len - = sizeof ( * rec ) ) {
if ( rec - > reserved ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" bad firmware, reserved field non-zero " ) ;
2019-10-31 23:20:07 -04:00
err = - EINVAL ;
} else {
regaddr = rec - > hiaddr < < 8 ;
regaddr | = rec - > loaddr ;
val = rec - > value ;
loaddr = rec - > loaddr ;
rec + + ;
err = check_and_set_masks ( idtcm , regaddr , val ) ;
}
2020-07-28 16:00:30 -04:00
if ( err ! = - EINVAL ) {
err = 0 ;
2019-10-31 23:20:07 -04:00
/* Top (status registers) and bottom are read-only */
2021-09-13 16:12:33 -04:00
if ( regaddr < GPIO_USER_CONTROL | | regaddr > = scratch )
2019-10-31 23:20:07 -04:00
continue ;
/* Page size 128, last 4 bytes of page skipped */
2021-02-17 00:42:18 -05:00
if ( ( loaddr > 0x7b & & loaddr < = 0x7f ) | | loaddr > 0xfb )
2019-10-31 23:20:07 -04:00
continue ;
err = idtcm_write ( idtcm , regaddr , 0 , & val , sizeof ( val ) ) ;
}
if ( err )
goto out ;
}
2020-07-28 16:00:30 -04:00
display_pll_and_masks ( idtcm ) ;
2019-10-31 23:20:07 -04:00
out :
release_firmware ( fw ) ;
return err ;
}
2020-07-28 16:00:30 -04:00
static int idtcm_output_enable ( struct idtcm_channel * channel ,
bool enable , unsigned int outn )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
2020-12-08 10:41:56 -05:00
int base ;
2019-10-31 23:20:07 -04:00
int err ;
2020-07-28 16:00:30 -04:00
u8 val ;
2019-10-31 23:20:07 -04:00
2021-09-13 16:12:33 -04:00
base = get_output_base_addr ( idtcm - > fw_ver , outn ) ;
2020-12-08 10:41:56 -05:00
if ( ! ( base > 0 ) ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2020-12-08 10:41:56 -05:00
" %s - Unsupported out%d " , __func__ , outn ) ;
return base ;
}
err = idtcm_read ( idtcm , ( u16 ) base , OUT_CTRL_1 , & val , sizeof ( val ) ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
if ( enable )
val | = SQUELCH_DISABLE ;
else
val & = ~ SQUELCH_DISABLE ;
2020-12-08 10:41:56 -05:00
return idtcm_write ( idtcm , ( u16 ) base , OUT_CTRL_1 , & val , sizeof ( val ) ) ;
2020-07-28 16:00:30 -04:00
}
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
static int idtcm_perout_enable ( struct idtcm_channel * channel ,
2021-09-24 15:01:32 -04:00
struct ptp_perout_request * perout ,
bool enable )
2020-07-28 16:00:30 -04:00
{
2021-02-17 00:42:13 -05:00
struct idtcm * idtcm = channel - > idtcm ;
struct timespec64 ts = { 0 , 0 } ;
int err ;
2020-07-28 16:00:30 -04:00
2022-05-16 10:47:07 -04:00
err = idtcm_output_enable ( channel , enable , perout - > index ) ;
2021-02-17 00:42:13 -05:00
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Unable to set output enable " ) ;
2021-02-17 00:42:13 -05:00
return err ;
}
2020-07-28 16:00:30 -04:00
2021-02-17 00:42:13 -05:00
/* Align output to internal 1 PPS */
return _idtcm_settime ( channel , & ts , SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS ) ;
2020-07-28 16:00:30 -04:00
}
2020-12-08 10:41:56 -05:00
static int idtcm_get_pll_mode ( struct idtcm_channel * channel ,
2021-09-13 16:12:34 -04:00
enum pll_mode * mode )
2020-12-08 10:41:56 -05:00
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
u8 dpll_mode ;
2021-09-13 16:12:33 -04:00
err = idtcm_read ( idtcm , channel - > dpll_n ,
IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_MODE ) ,
2020-12-08 10:41:56 -05:00
& dpll_mode , sizeof ( dpll_mode ) ) ;
if ( err )
return err ;
2021-09-13 16:12:34 -04:00
* mode = ( dpll_mode > > PLL_MODE_SHIFT ) & PLL_MODE_MASK ;
2020-12-08 10:41:56 -05:00
return 0 ;
}
2019-10-31 23:20:07 -04:00
static int idtcm_set_pll_mode ( struct idtcm_channel * channel ,
2021-09-13 16:12:34 -04:00
enum pll_mode mode )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
u8 dpll_mode ;
2021-09-13 16:12:33 -04:00
err = idtcm_read ( idtcm , channel - > dpll_n ,
IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_MODE ) ,
2019-10-31 23:20:07 -04:00
& dpll_mode , sizeof ( dpll_mode ) ) ;
if ( err )
return err ;
dpll_mode & = ~ ( PLL_MODE_MASK < < PLL_MODE_SHIFT ) ;
2021-09-13 16:12:34 -04:00
dpll_mode | = ( mode < < PLL_MODE_SHIFT ) ;
2019-10-31 23:20:07 -04:00
2021-09-13 16:12:33 -04:00
err = idtcm_write ( idtcm , channel - > dpll_n ,
IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_MODE ) ,
2019-10-31 23:20:07 -04:00
& dpll_mode , sizeof ( dpll_mode ) ) ;
2021-09-13 16:12:34 -04:00
return err ;
}
static int idtcm_get_manual_reference ( struct idtcm_channel * channel ,
enum manual_reference * ref )
{
struct idtcm * idtcm = channel - > idtcm ;
u8 dpll_manu_ref_cfg ;
int err ;
err = idtcm_read ( idtcm , channel - > dpll_ctrl_n ,
DPLL_CTRL_DPLL_MANU_REF_CFG ,
& dpll_manu_ref_cfg , sizeof ( dpll_manu_ref_cfg ) ) ;
if ( err )
return err ;
dpll_manu_ref_cfg & = ( MANUAL_REFERENCE_MASK < < MANUAL_REFERENCE_SHIFT ) ;
* ref = dpll_manu_ref_cfg > > MANUAL_REFERENCE_SHIFT ;
return 0 ;
}
static int idtcm_set_manual_reference ( struct idtcm_channel * channel ,
enum manual_reference ref )
{
struct idtcm * idtcm = channel - > idtcm ;
u8 dpll_manu_ref_cfg ;
int err ;
err = idtcm_read ( idtcm , channel - > dpll_ctrl_n ,
DPLL_CTRL_DPLL_MANU_REF_CFG ,
& dpll_manu_ref_cfg , sizeof ( dpll_manu_ref_cfg ) ) ;
if ( err )
return err ;
dpll_manu_ref_cfg & = ~ ( MANUAL_REFERENCE_MASK < < MANUAL_REFERENCE_SHIFT ) ;
dpll_manu_ref_cfg | = ( ref < < MANUAL_REFERENCE_SHIFT ) ;
err = idtcm_write ( idtcm , channel - > dpll_ctrl_n ,
DPLL_CTRL_DPLL_MANU_REF_CFG ,
& dpll_manu_ref_cfg , sizeof ( dpll_manu_ref_cfg ) ) ;
return err ;
}
static int configure_dpll_mode_write_frequency ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
err = idtcm_set_pll_mode ( channel , PLL_MODE_WRITE_FREQUENCY ) ;
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Failed to set pll mode to write frequency " ) ;
2021-09-13 16:12:34 -04:00
else
channel - > mode = PTP_PLL_MODE_WRITE_FREQUENCY ;
return err ;
}
static int configure_dpll_mode_write_phase ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
err = idtcm_set_pll_mode ( channel , PLL_MODE_WRITE_PHASE ) ;
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Failed to set pll mode to write phase " ) ;
2021-09-13 16:12:34 -04:00
else
channel - > mode = PTP_PLL_MODE_WRITE_PHASE ;
return err ;
}
static int configure_manual_reference_write_frequency ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
err = idtcm_set_manual_reference ( channel , MANU_REF_WRITE_FREQUENCY ) ;
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Failed to set manual reference to write frequency " ) ;
2021-09-13 16:12:34 -04:00
else
channel - > mode = PTP_PLL_MODE_WRITE_FREQUENCY ;
return err ;
}
static int configure_manual_reference_write_phase ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
err = idtcm_set_manual_reference ( channel , MANU_REF_WRITE_PHASE ) ;
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Failed to set manual reference to write phase " ) ;
2021-09-13 16:12:34 -04:00
else
channel - > mode = PTP_PLL_MODE_WRITE_PHASE ;
return err ;
}
static int idtcm_stop_phase_pull_in ( struct idtcm_channel * channel )
{
int err ;
err = _idtcm_adjfine ( channel , channel - > current_freq_scaled_ppm ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
2021-09-13 16:12:34 -04:00
channel - > phase_pull_in = false ;
2019-10-31 23:20:07 -04:00
return 0 ;
}
2021-09-13 16:12:34 -04:00
static long idtcm_work_handler ( struct ptp_clock_info * ptp )
{
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
struct idtcm * idtcm = channel - > idtcm ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2021-09-13 16:12:34 -04:00
( void ) idtcm_stop_phase_pull_in ( channel ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2021-09-13 16:12:34 -04:00
/* Return a negative value here to not reschedule */
return - 1 ;
}
static s32 phase_pull_in_scaled_ppm ( s32 current_ppm , s32 phase_pull_in_ppb )
{
/* ppb = scaled_ppm * 125 / 2^13 */
/* scaled_ppm = ppb * 2^13 / 125 */
2021-09-24 15:01:32 -04:00
s64 max_scaled_ppm = div_s64 ( ( s64 ) PHASE_PULL_IN_MAX_PPB < < 13 , 125 ) ;
s64 scaled_ppm = div_s64 ( ( s64 ) phase_pull_in_ppb < < 13 , 125 ) ;
2021-09-13 16:12:34 -04:00
current_ppm + = scaled_ppm ;
if ( current_ppm > max_scaled_ppm )
current_ppm = max_scaled_ppm ;
else if ( current_ppm < - max_scaled_ppm )
current_ppm = - max_scaled_ppm ;
return current_ppm ;
}
static int do_phase_pull_in_sw ( struct idtcm_channel * channel ,
s32 delta_ns ,
u32 max_ffo_ppb )
{
s32 current_ppm = channel - > current_freq_scaled_ppm ;
u32 duration_ms = MSEC_PER_SEC ;
s32 delta_ppm ;
s32 ppb ;
int err ;
/* If the ToD correction is less than PHASE_PULL_IN_MIN_THRESHOLD_NS,
* skip . The error introduced by the ToD adjustment procedure would
* be bigger than the required ToD correction
*/
if ( abs ( delta_ns ) < PHASE_PULL_IN_MIN_THRESHOLD_NS )
return 0 ;
if ( max_ffo_ppb = = 0 )
max_ffo_ppb = PHASE_PULL_IN_MAX_PPB ;
/* For most cases, keep phase pull-in duration 1 second */
ppb = delta_ns ;
while ( abs ( ppb ) > max_ffo_ppb ) {
duration_ms * = 2 ;
ppb / = 2 ;
}
delta_ppm = phase_pull_in_scaled_ppm ( current_ppm , ppb ) ;
err = _idtcm_adjfine ( channel , delta_ppm ) ;
if ( err )
return err ;
/* schedule the worker to cancel phase pull-in */
ptp_schedule_worker ( channel - > ptp_clock ,
msecs_to_jiffies ( duration_ms ) - 1 ) ;
channel - > phase_pull_in = true ;
return 0 ;
}
static int initialize_operating_mode_with_manual_reference ( struct idtcm_channel * channel ,
enum manual_reference ref )
{
struct idtcm * idtcm = channel - > idtcm ;
channel - > mode = PTP_PLL_MODE_UNSUPPORTED ;
channel - > configure_write_frequency = configure_manual_reference_write_frequency ;
channel - > configure_write_phase = configure_manual_reference_write_phase ;
channel - > do_phase_pull_in = do_phase_pull_in_sw ;
switch ( ref ) {
case MANU_REF_WRITE_PHASE :
channel - > mode = PTP_PLL_MODE_WRITE_PHASE ;
break ;
case MANU_REF_WRITE_FREQUENCY :
channel - > mode = PTP_PLL_MODE_WRITE_FREQUENCY ;
break ;
default :
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev ,
2021-09-13 16:12:34 -04:00
" Unsupported MANUAL_REFERENCE: 0x%02x " , ref ) ;
}
return 0 ;
}
static int initialize_operating_mode_with_pll_mode ( struct idtcm_channel * channel ,
enum pll_mode mode )
{
struct idtcm * idtcm = channel - > idtcm ;
int err = 0 ;
channel - > mode = PTP_PLL_MODE_UNSUPPORTED ;
channel - > configure_write_frequency = configure_dpll_mode_write_frequency ;
channel - > configure_write_phase = configure_dpll_mode_write_phase ;
channel - > do_phase_pull_in = do_phase_pull_in_fw ;
switch ( mode ) {
case PLL_MODE_WRITE_PHASE :
channel - > mode = PTP_PLL_MODE_WRITE_PHASE ;
break ;
case PLL_MODE_WRITE_FREQUENCY :
channel - > mode = PTP_PLL_MODE_WRITE_FREQUENCY ;
break ;
default :
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-09-13 16:12:34 -04:00
" Unsupported PLL_MODE: 0x%02x " , mode ) ;
err = - EINVAL ;
}
return err ;
}
static int initialize_dco_operating_mode ( struct idtcm_channel * channel )
{
enum manual_reference ref = MANU_REF_XO_DPLL ;
enum pll_mode mode = PLL_MODE_DISABLED ;
struct idtcm * idtcm = channel - > idtcm ;
int err ;
channel - > mode = PTP_PLL_MODE_UNSUPPORTED ;
err = idtcm_get_pll_mode ( channel , & mode ) ;
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Unable to read pll mode! " ) ;
2021-09-13 16:12:34 -04:00
return err ;
}
if ( mode = = PLL_MODE_PLL ) {
err = idtcm_get_manual_reference ( channel , & ref ) ;
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Unable to read manual reference! " ) ;
2021-09-13 16:12:34 -04:00
return err ;
}
err = initialize_operating_mode_with_manual_reference ( channel , ref ) ;
} else {
err = initialize_operating_mode_with_pll_mode ( channel , mode ) ;
}
if ( channel - > mode = = PTP_PLL_MODE_WRITE_PHASE )
channel - > configure_write_frequency ( channel ) ;
return err ;
}
2019-10-31 23:20:07 -04:00
/* PTP Hardware Clock interface */
2021-11-11 07:50:34 -08:00
/*
2023-06-12 14:14:58 -07:00
* Maximum absolute value for write phase offset in nanoseconds
2022-05-16 10:47:06 -04:00
*
2020-05-01 23:35:38 -04:00
* Destination signed register is 32 - bit register in resolution of 50 ps
*
2023-06-12 14:14:58 -07:00
* 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 ps
* Represent 107374182350 ps as 107374182 ns
*/
static s32 idtcm_getmaxphase ( struct ptp_clock_info * ptp __always_unused )
{
return MAX_ABS_WRITE_PHASE_NANOSECONDS ;
}
/*
* Internal function for implementing support for write phase offset
*
* @ channel : channel
* @ delta_ns : delta in nanoseconds
2020-05-01 23:35:38 -04:00
*/
static int _idtcm_adjphase ( struct idtcm_channel * channel , s32 delta_ns )
{
struct idtcm * idtcm = channel - > idtcm ;
int err ;
u8 i ;
u8 buf [ 4 ] = { 0 } ;
s32 phase_50ps ;
2021-09-13 16:12:34 -04:00
if ( channel - > mode ! = PTP_PLL_MODE_WRITE_PHASE ) {
err = channel - > configure_write_phase ( channel ) ;
2020-05-01 23:35:38 -04:00
if ( err )
return err ;
}
2023-06-12 14:14:58 -07:00
phase_50ps = div_s64 ( ( s64 ) delta_ns * 1000 , 50 ) ;
2020-05-01 23:35:38 -04:00
for ( i = 0 ; i < 4 ; i + + ) {
buf [ i ] = phase_50ps & 0xff ;
phase_50ps > > = 8 ;
}
err = idtcm_write ( idtcm , channel - > dpll_phase , DPLL_WR_PHASE ,
buf , sizeof ( buf ) ) ;
return err ;
}
2020-07-28 16:00:30 -04:00
static int _idtcm_adjfine ( struct idtcm_channel * channel , long scaled_ppm )
2019-10-31 23:20:07 -04:00
{
struct idtcm * idtcm = channel - > idtcm ;
u8 i ;
int err ;
u8 buf [ 6 ] = { 0 } ;
s64 fcw ;
2021-09-13 16:12:34 -04:00
if ( channel - > mode ! = PTP_PLL_MODE_WRITE_FREQUENCY ) {
err = channel - > configure_write_frequency ( channel ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
}
/*
* Frequency Control Word unit is : 1.11 * 10 ^ - 10 ppm
*
* adjfreq :
* ppb * 10 ^ 9
* FCW = - - - - - - - - - -
* 111
*
* adjfine :
* ppm_16 * 5 ^ 12
* FCW = - - - - - - - - - - - - -
* 111 * 2 ^ 4
*/
/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
2020-07-28 16:00:30 -04:00
fcw = scaled_ppm * 244140625ULL ;
2019-10-31 23:20:07 -04:00
2020-12-08 10:41:56 -05:00
fcw = div_s64 ( fcw , 1776 ) ;
2019-10-31 23:20:07 -04:00
for ( i = 0 ; i < 6 ; i + + ) {
buf [ i ] = fcw & 0xff ;
fcw > > = 8 ;
}
err = idtcm_write ( idtcm , channel - > dpll_freq , DPLL_WR_FREQ ,
buf , sizeof ( buf ) ) ;
return err ;
}
static int idtcm_gettime ( struct ptp_clock_info * ptp , struct timespec64 * ts )
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2019-10-31 23:20:07 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
err = _idtcm_gettime_immediate ( channel , ts ) ;
mutex_unlock ( idtcm - > lock ) ;
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev , " Failed at line %d in %s! " ,
2021-02-17 00:42:15 -05:00
__LINE__ , __func__ ) ;
2020-07-28 16:00:30 -04:00
2019-10-31 23:20:07 -04:00
return err ;
}
2020-12-08 10:41:57 -05:00
static int idtcm_settime_deprecated ( struct ptp_clock_info * ptp ,
const struct timespec64 * ts )
2019-10-31 23:20:07 -04:00
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2019-10-31 23:20:07 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2020-12-08 10:41:57 -05:00
err = _idtcm_settime_deprecated ( channel , ts ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2020-07-28 16:00:30 -04:00
2019-10-31 23:20:07 -04:00
return err ;
}
2020-12-08 10:41:57 -05:00
static int idtcm_settime ( struct ptp_clock_info * ptp ,
2020-07-28 16:00:30 -04:00
const struct timespec64 * ts )
2019-10-31 23:20:07 -04:00
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2019-10-31 23:20:07 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2020-12-08 10:41:57 -05:00
err = _idtcm_settime ( channel , ts , SCSR_TOD_WR_TYPE_SEL_ABSOLUTE ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2019-10-31 23:20:07 -04:00
return err ;
}
2020-12-08 10:41:57 -05:00
static int idtcm_adjtime_deprecated ( struct ptp_clock_info * ptp , s64 delta )
2020-07-28 16:00:30 -04:00
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2020-07-28 16:00:30 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2020-12-08 10:41:57 -05:00
err = _idtcm_adjtime_deprecated ( channel , delta ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2020-07-28 16:00:30 -04:00
return err ;
}
2020-12-08 10:41:57 -05:00
static int idtcm_adjtime ( struct ptp_clock_info * ptp , s64 delta )
2020-07-28 16:00:30 -04:00
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2020-07-28 16:00:30 -04:00
struct idtcm * idtcm = channel - > idtcm ;
struct timespec64 ts ;
enum scsr_tod_write_type_sel type ;
int err ;
2021-09-13 16:12:34 -04:00
if ( channel - > phase_pull_in = = true )
2022-05-16 10:47:07 -04:00
return - EBUSY ;
2021-09-13 16:12:34 -04:00
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2021-09-13 16:12:34 -04:00
2020-12-08 10:41:57 -05:00
if ( abs ( delta ) < PHASE_PULL_IN_THRESHOLD_NS ) {
2021-09-13 16:12:34 -04:00
err = channel - > do_phase_pull_in ( channel , delta , 0 ) ;
2020-07-28 16:00:30 -04:00
} else {
2021-09-13 16:12:34 -04:00
if ( delta > = 0 ) {
ts = ns_to_timespec64 ( delta ) ;
type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS ;
} else {
ts = ns_to_timespec64 ( - delta ) ;
type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS ;
}
err = _idtcm_settime ( channel , & ts , type ) ;
2020-07-28 16:00:30 -04:00
}
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
if ( err )
dev_err ( idtcm - > dev ,
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2020-07-28 16:00:30 -04:00
return err ;
}
static int idtcm_adjphase ( struct ptp_clock_info * ptp , s32 delta )
2020-05-01 23:35:38 -04:00
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2020-05-01 23:35:38 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2020-05-01 23:35:38 -04:00
err = _idtcm_adjphase ( channel , delta ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2020-07-28 16:00:30 -04:00
return err ;
}
static int idtcm_adjfine ( struct ptp_clock_info * ptp , long scaled_ppm )
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2020-07-28 16:00:30 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err ;
2021-09-13 16:12:34 -04:00
if ( channel - > phase_pull_in = = true )
return 0 ;
if ( scaled_ppm = = channel - > current_freq_scaled_ppm )
return 0 ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
err = _idtcm_adjfine ( channel , scaled_ppm ) ;
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2020-07-28 16:00:30 -04:00
2021-09-24 15:01:32 -04:00
if ( err )
dev_err ( idtcm - > dev ,
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
else
2021-09-13 16:12:34 -04:00
channel - > current_freq_scaled_ppm = scaled_ppm ;
2020-05-01 23:35:38 -04:00
return err ;
}
2019-10-31 23:20:07 -04:00
static int idtcm_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
2021-02-17 00:42:16 -05:00
struct idtcm_channel * channel = container_of ( ptp , struct idtcm_channel , caps ) ;
2021-09-24 15:01:32 -04:00
struct idtcm * idtcm = channel - > idtcm ;
int err = - EOPNOTSUPP ;
mutex_lock ( idtcm - > lock ) ;
2019-10-31 23:20:07 -04:00
switch ( rq - > type ) {
case PTP_CLK_REQ_PEROUT :
2021-09-24 15:01:32 -04:00
if ( ! on )
err = idtcm_perout_enable ( channel , & rq - > perout , false ) ;
2019-10-31 23:20:07 -04:00
/* Only accept a 1-PPS aligned to the second. */
2021-09-24 15:01:32 -04:00
else if ( rq - > perout . start . nsec | | rq - > perout . period . sec ! = 1 | |
rq - > perout . period . nsec )
err = - ERANGE ;
else
err = idtcm_perout_enable ( channel , & rq - > perout , true ) ;
break ;
case PTP_CLK_REQ_EXTTS :
2022-05-16 10:47:06 -04:00
err = idtcm_extts_enable ( channel , rq , on ) ;
2021-09-24 15:01:32 -04:00
break ;
2019-10-31 23:20:07 -04:00
default :
break ;
}
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
if ( err )
dev_err ( channel - > idtcm - > dev ,
" Failed in %s with err %d! " , __func__ , err ) ;
return err ;
2019-10-31 23:20:07 -04:00
}
2020-07-28 16:00:30 -04:00
static int idtcm_enable_tod ( struct idtcm_channel * channel )
{
struct idtcm * idtcm = channel - > idtcm ;
struct timespec64 ts = { 0 , 0 } ;
2021-09-13 16:12:33 -04:00
u16 tod_cfg = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , TOD_CFG ) ;
2020-07-28 16:00:30 -04:00
u8 cfg ;
int err ;
2019-10-31 23:20:07 -04:00
/*
* Start the TOD clock ticking .
*/
2021-09-13 16:12:33 -04:00
err = idtcm_read ( idtcm , channel - > tod_n , tod_cfg , & cfg , sizeof ( cfg ) ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
cfg | = TOD_ENABLE ;
2021-09-13 16:12:33 -04:00
err = idtcm_write ( idtcm , channel - > tod_n , tod_cfg , & cfg , sizeof ( cfg ) ) ;
2019-10-31 23:20:07 -04:00
if ( err )
return err ;
2021-09-13 16:12:33 -04:00
if ( idtcm - > fw_ver < V487 )
2020-12-08 10:41:57 -05:00
return _idtcm_settime_deprecated ( channel , & ts ) ;
else
return _idtcm_settime ( channel , & ts ,
SCSR_TOD_WR_TYPE_SEL_ABSOLUTE ) ;
2019-10-31 23:20:07 -04:00
}
2020-12-08 10:41:57 -05:00
static void idtcm_set_version_info ( struct idtcm * idtcm )
2019-10-31 23:20:07 -04:00
{
u8 major ;
u8 minor ;
u8 hotfix ;
u16 product_id ;
u8 hw_rev_id ;
2020-01-07 09:47:57 -05:00
u8 config_select ;
2019-10-31 23:20:07 -04:00
idtcm_read_major_release ( idtcm , & major ) ;
idtcm_read_minor_release ( idtcm , & minor ) ;
idtcm_read_hotfix_release ( idtcm , & hotfix ) ;
idtcm_read_product_id ( idtcm , & product_id ) ;
idtcm_read_hw_rev_id ( idtcm , & hw_rev_id ) ;
2020-01-07 09:47:57 -05:00
idtcm_read_otp_scsr_config_select ( idtcm , & config_select ) ;
2020-07-28 16:00:30 -04:00
snprintf ( idtcm - > version , sizeof ( idtcm - > version ) , " %u.%u.%u " ,
major , minor , hotfix ) ;
2021-09-13 16:12:33 -04:00
idtcm - > fw_ver = idtcm_fw_version ( idtcm - > version ) ;
2020-12-08 10:41:57 -05:00
2021-09-24 15:01:32 -04:00
dev_info ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" %d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d " ,
major , minor , hotfix ,
2020-01-07 09:47:57 -05:00
product_id , hw_rev_id , config_select ) ;
2019-10-31 23:20:07 -04:00
}
2022-05-16 10:47:06 -04:00
static int idtcm_verify_pin ( struct ptp_clock_info * ptp , unsigned int pin ,
enum ptp_pin_function func , unsigned int chan )
{
switch ( func ) {
case PTP_PF_NONE :
case PTP_PF_EXTTS :
break ;
case PTP_PF_PEROUT :
case PTP_PF_PHYSYNC :
return - 1 ;
}
return 0 ;
}
static struct ptp_pin_desc pin_config [ MAX_TOD ] [ MAX_REF_CLK ] ;
2020-12-08 10:41:57 -05:00
static const struct ptp_clock_info idtcm_caps = {
2020-07-28 16:00:30 -04:00
. owner = THIS_MODULE ,
. max_adj = 244000 ,
. n_per_out = 12 ,
2021-09-24 15:01:32 -04:00
. n_ext_ts = MAX_TOD ,
2022-05-16 10:47:06 -04:00
. n_pins = MAX_REF_CLK ,
2020-07-28 16:00:30 -04:00
. adjphase = & idtcm_adjphase ,
2023-06-12 14:14:58 -07:00
. getmaxphase = & idtcm_getmaxphase ,
2020-07-28 16:00:30 -04:00
. adjfine = & idtcm_adjfine ,
2020-12-08 10:41:57 -05:00
. adjtime = & idtcm_adjtime ,
2020-07-28 16:00:30 -04:00
. gettime64 = & idtcm_gettime ,
2020-12-08 10:41:57 -05:00
. settime64 = & idtcm_settime ,
2020-07-28 16:00:30 -04:00
. enable = & idtcm_enable ,
2022-05-16 10:47:06 -04:00
. verify = & idtcm_verify_pin ,
2021-09-13 16:12:34 -04:00
. do_aux_work = & idtcm_work_handler ,
2020-07-28 16:00:30 -04:00
} ;
2020-12-08 10:41:57 -05:00
static const struct ptp_clock_info idtcm_caps_deprecated = {
2019-10-31 23:20:07 -04:00
. owner = THIS_MODULE ,
. max_adj = 244000 ,
2020-07-28 16:00:30 -04:00
. n_per_out = 12 ,
2021-09-24 15:01:32 -04:00
. n_ext_ts = MAX_TOD ,
2022-05-16 10:47:06 -04:00
. n_pins = MAX_REF_CLK ,
2020-05-01 23:35:38 -04:00
. adjphase = & idtcm_adjphase ,
2023-06-12 14:14:58 -07:00
. getmaxphase = & idtcm_getmaxphase ,
2020-07-28 16:00:30 -04:00
. adjfine = & idtcm_adjfine ,
2020-12-08 10:41:57 -05:00
. adjtime = & idtcm_adjtime_deprecated ,
2019-10-31 23:20:07 -04:00
. gettime64 = & idtcm_gettime ,
2020-12-08 10:41:57 -05:00
. settime64 = & idtcm_settime_deprecated ,
2019-10-31 23:20:07 -04:00
. enable = & idtcm_enable ,
2022-05-16 10:47:06 -04:00
. verify = & idtcm_verify_pin ,
2021-09-13 16:12:34 -04:00
. do_aux_work = & idtcm_work_handler ,
2019-10-31 23:20:07 -04:00
} ;
2020-07-28 16:00:30 -04:00
static int configure_channel_pll ( struct idtcm_channel * channel )
2019-10-31 23:20:07 -04:00
{
2021-09-13 16:12:33 -04:00
struct idtcm * idtcm = channel - > idtcm ;
2020-07-28 16:00:30 -04:00
int err = 0 ;
2019-10-31 23:20:07 -04:00
2020-07-28 16:00:30 -04:00
switch ( channel - > pll ) {
2019-10-31 23:20:07 -04:00
case 0 :
channel - > dpll_freq = DPLL_FREQ_0 ;
channel - > dpll_n = DPLL_0 ;
channel - > hw_dpll_n = HW_DPLL_0 ;
channel - > dpll_phase = DPLL_PHASE_0 ;
channel - > dpll_ctrl_n = DPLL_CTRL_0 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0 ;
break ;
case 1 :
channel - > dpll_freq = DPLL_FREQ_1 ;
channel - > dpll_n = DPLL_1 ;
channel - > hw_dpll_n = HW_DPLL_1 ;
channel - > dpll_phase = DPLL_PHASE_1 ;
channel - > dpll_ctrl_n = DPLL_CTRL_1 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1 ;
break ;
case 2 :
channel - > dpll_freq = DPLL_FREQ_2 ;
2021-09-13 16:12:33 -04:00
channel - > dpll_n = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_2 ) ;
2019-10-31 23:20:07 -04:00
channel - > hw_dpll_n = HW_DPLL_2 ;
channel - > dpll_phase = DPLL_PHASE_2 ;
channel - > dpll_ctrl_n = DPLL_CTRL_2 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2 ;
break ;
case 3 :
channel - > dpll_freq = DPLL_FREQ_3 ;
channel - > dpll_n = DPLL_3 ;
channel - > hw_dpll_n = HW_DPLL_3 ;
channel - > dpll_phase = DPLL_PHASE_3 ;
channel - > dpll_ctrl_n = DPLL_CTRL_3 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3 ;
break ;
2020-07-28 16:00:30 -04:00
case 4 :
channel - > dpll_freq = DPLL_FREQ_4 ;
2021-09-13 16:12:33 -04:00
channel - > dpll_n = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_4 ) ;
2020-07-28 16:00:30 -04:00
channel - > hw_dpll_n = HW_DPLL_4 ;
channel - > dpll_phase = DPLL_PHASE_4 ;
channel - > dpll_ctrl_n = DPLL_CTRL_4 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4 ;
break ;
case 5 :
channel - > dpll_freq = DPLL_FREQ_5 ;
channel - > dpll_n = DPLL_5 ;
channel - > hw_dpll_n = HW_DPLL_5 ;
channel - > dpll_phase = DPLL_PHASE_5 ;
channel - > dpll_ctrl_n = DPLL_CTRL_5 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5 ;
break ;
case 6 :
channel - > dpll_freq = DPLL_FREQ_6 ;
2021-09-13 16:12:33 -04:00
channel - > dpll_n = IDTCM_FW_REG ( idtcm - > fw_ver , V520 , DPLL_6 ) ;
2020-07-28 16:00:30 -04:00
channel - > hw_dpll_n = HW_DPLL_6 ;
channel - > dpll_phase = DPLL_PHASE_6 ;
channel - > dpll_ctrl_n = DPLL_CTRL_6 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6 ;
break ;
case 7 :
channel - > dpll_freq = DPLL_FREQ_7 ;
channel - > dpll_n = DPLL_7 ;
channel - > hw_dpll_n = HW_DPLL_7 ;
channel - > dpll_phase = DPLL_PHASE_7 ;
channel - > dpll_ctrl_n = DPLL_CTRL_7 ;
channel - > dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7 ;
break ;
default :
err = - EINVAL ;
}
return err ;
}
2021-09-24 15:01:32 -04:00
/*
* Compensate for the PTP DCO input - to - output delay .
* This delay is 18 FOD cycles .
*/
static u32 idtcm_get_dco_delay ( struct idtcm_channel * channel )
2020-07-28 16:00:30 -04:00
{
2021-09-24 15:01:32 -04:00
struct idtcm * idtcm = channel - > idtcm ;
u8 mbuf [ 8 ] = { 0 } ;
u8 nbuf [ 2 ] = { 0 } ;
u32 fodFreq ;
2020-07-28 16:00:30 -04:00
int err ;
2021-09-24 15:01:32 -04:00
u64 m ;
u16 n ;
2020-07-28 16:00:30 -04:00
2021-09-24 15:01:32 -04:00
err = idtcm_read ( idtcm , channel - > dpll_ctrl_n ,
DPLL_CTRL_DPLL_FOD_FREQ , mbuf , 6 ) ;
if ( err )
return 0 ;
2020-07-28 16:00:30 -04:00
2021-09-24 15:01:32 -04:00
err = idtcm_read ( idtcm , channel - > dpll_ctrl_n ,
DPLL_CTRL_DPLL_FOD_FREQ + 6 , nbuf , 2 ) ;
if ( err )
return 0 ;
2021-09-13 16:12:34 -04:00
2021-09-24 15:01:32 -04:00
m = get_unaligned_le64 ( mbuf ) ;
n = get_unaligned_le16 ( nbuf ) ;
2020-07-28 16:00:30 -04:00
2021-09-24 15:01:32 -04:00
if ( n = = 0 )
n = 1 ;
fodFreq = ( u32 ) div_u64 ( m , n ) ;
2022-05-16 10:47:06 -04:00
2021-09-24 15:01:32 -04:00
if ( fodFreq > = 500000000 )
2022-05-16 10:47:06 -04:00
return ( u32 ) div_u64 ( 18 * ( u64 ) NSEC_PER_SEC , fodFreq ) ;
2021-09-24 15:01:32 -04:00
return 0 ;
}
static int configure_channel_tod ( struct idtcm_channel * channel , u32 index )
{
enum fw_version fw_ver = channel - > idtcm - > fw_ver ;
2020-07-28 16:00:30 -04:00
/* Set tod addresses */
switch ( index ) {
case 0 :
2021-09-13 16:12:33 -04:00
channel - > tod_read_primary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_PRIMARY_0 ) ;
2022-05-16 10:47:06 -04:00
channel - > tod_read_secondary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_SECONDARY_0 ) ;
2021-09-13 16:12:33 -04:00
channel - > tod_write = IDTCM_FW_REG ( fw_ver , V520 , TOD_WRITE_0 ) ;
channel - > tod_n = IDTCM_FW_REG ( fw_ver , V520 , TOD_0 ) ;
channel - > sync_src = SYNC_SOURCE_DPLL0_TOD_PPS ;
2020-07-28 16:00:30 -04:00
break ;
case 1 :
2021-09-13 16:12:33 -04:00
channel - > tod_read_primary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_PRIMARY_1 ) ;
2022-05-16 10:47:06 -04:00
channel - > tod_read_secondary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_SECONDARY_1 ) ;
2021-09-13 16:12:33 -04:00
channel - > tod_write = IDTCM_FW_REG ( fw_ver , V520 , TOD_WRITE_1 ) ;
channel - > tod_n = IDTCM_FW_REG ( fw_ver , V520 , TOD_1 ) ;
channel - > sync_src = SYNC_SOURCE_DPLL1_TOD_PPS ;
2020-07-28 16:00:30 -04:00
break ;
case 2 :
2021-09-13 16:12:33 -04:00
channel - > tod_read_primary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_PRIMARY_2 ) ;
2022-05-16 10:47:06 -04:00
channel - > tod_read_secondary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_SECONDARY_2 ) ;
2021-09-13 16:12:33 -04:00
channel - > tod_write = IDTCM_FW_REG ( fw_ver , V520 , TOD_WRITE_2 ) ;
channel - > tod_n = IDTCM_FW_REG ( fw_ver , V520 , TOD_2 ) ;
channel - > sync_src = SYNC_SOURCE_DPLL2_TOD_PPS ;
2020-07-28 16:00:30 -04:00
break ;
case 3 :
2021-09-13 16:12:33 -04:00
channel - > tod_read_primary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_PRIMARY_3 ) ;
2022-05-16 10:47:06 -04:00
channel - > tod_read_secondary = IDTCM_FW_REG ( fw_ver , V520 , TOD_READ_SECONDARY_3 ) ;
2021-09-13 16:12:33 -04:00
channel - > tod_write = IDTCM_FW_REG ( fw_ver , V520 , TOD_WRITE_3 ) ;
channel - > tod_n = IDTCM_FW_REG ( fw_ver , V520 , TOD_3 ) ;
channel - > sync_src = SYNC_SOURCE_DPLL3_TOD_PPS ;
2020-07-28 16:00:30 -04:00
break ;
2019-10-31 23:20:07 -04:00
default :
return - EINVAL ;
}
2021-09-24 15:01:32 -04:00
return 0 ;
}
static int idtcm_enable_channel ( struct idtcm * idtcm , u32 index )
{
struct idtcm_channel * channel ;
int err ;
2022-05-16 10:47:06 -04:00
int i ;
2021-09-24 15:01:32 -04:00
if ( ! ( index < MAX_TOD ) )
return - EINVAL ;
channel = & idtcm - > channel [ index ] ;
channel - > idtcm = idtcm ;
channel - > current_freq_scaled_ppm = 0 ;
/* Set pll addresses */
err = configure_channel_pll ( channel ) ;
if ( err )
return err ;
/* Set tod addresses */
err = configure_channel_tod ( channel , index ) ;
if ( err )
return err ;
2021-09-13 16:12:33 -04:00
if ( idtcm - > fw_ver < V487 )
2020-12-08 10:41:57 -05:00
channel - > caps = idtcm_caps_deprecated ;
2020-07-28 16:00:30 -04:00
else
channel - > caps = idtcm_caps ;
2019-10-31 23:20:07 -04:00
snprintf ( channel - > caps . name , sizeof ( channel - > caps . name ) ,
2020-07-28 16:00:30 -04:00
" IDT CM TOD%u " , index ) ;
2022-05-16 10:47:06 -04:00
channel - > caps . pin_config = pin_config [ index ] ;
for ( i = 0 ; i < channel - > caps . n_pins ; + + i ) {
struct ptp_pin_desc * ppd = & channel - > caps . pin_config [ i ] ;
snprintf ( ppd - > name , sizeof ( ppd - > name ) , " input_ref%d " , i ) ;
ppd - > index = i ;
ppd - > func = PTP_PF_NONE ;
ppd - > chan = index ;
}
2021-09-13 16:12:34 -04:00
err = initialize_dco_operating_mode ( channel ) ;
if ( err )
2019-10-31 23:20:07 -04:00
return err ;
err = idtcm_enable_tod ( channel ) ;
2020-07-28 16:00:30 -04:00
if ( err ) {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" Failed at line %d in %s! " , __LINE__ , __func__ ) ;
2019-10-31 23:20:07 -04:00
return err ;
2020-07-28 16:00:30 -04:00
}
2019-10-31 23:20:07 -04:00
2021-09-24 15:01:32 -04:00
channel - > dco_delay = idtcm_get_dco_delay ( channel ) ;
2019-10-31 23:20:07 -04:00
channel - > ptp_clock = ptp_clock_register ( & channel - > caps , NULL ) ;
if ( IS_ERR ( channel - > ptp_clock ) ) {
err = PTR_ERR ( channel - > ptp_clock ) ;
channel - > ptp_clock = NULL ;
return err ;
}
if ( ! channel - > ptp_clock )
return - ENOTSUPP ;
2021-09-24 15:01:32 -04:00
dev_info ( idtcm - > dev , " PLL%d registered as ptp%d " ,
2019-10-31 23:20:07 -04:00
index , channel - > ptp_clock - > index ) ;
return 0 ;
}
2021-09-24 15:01:32 -04:00
static int idtcm_enable_extts_channel ( struct idtcm * idtcm , u32 index )
{
struct idtcm_channel * channel ;
int err ;
if ( ! ( index < MAX_TOD ) )
return - EINVAL ;
channel = & idtcm - > channel [ index ] ;
channel - > idtcm = idtcm ;
/* Set tod addresses */
err = configure_channel_tod ( channel , index ) ;
if ( err )
return err ;
channel - > idtcm = idtcm ;
return 0 ;
}
static void idtcm_extts_check ( struct work_struct * work )
{
struct idtcm * idtcm = container_of ( work , struct idtcm , extts_work . work ) ;
2022-05-16 10:47:06 -04:00
struct idtcm_channel * channel ;
u8 mask ;
int err ;
int i ;
2021-09-24 15:01:32 -04:00
if ( idtcm - > extts_mask = = 0 )
return ;
mutex_lock ( idtcm - > lock ) ;
2022-05-16 10:47:06 -04:00
2021-09-24 15:01:32 -04:00
for ( i = 0 ; i < MAX_TOD ; i + + ) {
2022-05-16 10:47:06 -04:00
mask = 1 < < i ;
if ( ( idtcm - > extts_mask & mask ) = = 0 )
continue ;
err = idtcm_extts_check_channel ( idtcm , i ) ;
2021-09-24 15:01:32 -04:00
2022-05-16 10:47:06 -04:00
if ( err = = 0 ) {
2021-09-24 15:01:32 -04:00
/* trigger clears itself, so clear the mask */
2022-05-16 10:47:06 -04:00
if ( idtcm - > extts_single_shot ) {
2021-09-24 15:01:32 -04:00
idtcm - > extts_mask & = ~ mask ;
2022-05-16 10:47:06 -04:00
} else {
/* Re-arm */
channel = & idtcm - > channel [ i ] ;
arm_tod_read_trig_sel_refclk ( channel , channel - > refn ) ;
}
2021-09-24 15:01:32 -04:00
}
}
if ( idtcm - > extts_mask )
schedule_delayed_work ( & idtcm - > extts_work ,
msecs_to_jiffies ( EXTTS_PERIOD_MS ) ) ;
2022-05-16 10:47:06 -04:00
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
}
2019-10-31 23:20:07 -04:00
static void ptp_clock_unregister_all ( struct idtcm * idtcm )
{
u8 i ;
struct idtcm_channel * channel ;
2020-07-28 16:00:30 -04:00
for ( i = 0 ; i < MAX_TOD ; i + + ) {
2019-10-31 23:20:07 -04:00
channel = & idtcm - > channel [ i ] ;
if ( channel - > ptp_clock )
ptp_clock_unregister ( channel - > ptp_clock ) ;
}
}
static void set_default_masks ( struct idtcm * idtcm )
{
2020-07-28 16:00:30 -04:00
idtcm - > tod_mask = DEFAULT_TOD_MASK ;
2021-09-24 15:01:32 -04:00
idtcm - > extts_mask = 0 ;
2020-07-28 16:00:30 -04:00
2022-05-16 10:47:06 -04:00
idtcm - > channel [ 0 ] . tod = 0 ;
idtcm - > channel [ 1 ] . tod = 1 ;
idtcm - > channel [ 2 ] . tod = 2 ;
idtcm - > channel [ 3 ] . tod = 3 ;
2020-07-28 16:00:30 -04:00
idtcm - > channel [ 0 ] . pll = DEFAULT_TOD0_PTP_PLL ;
idtcm - > channel [ 1 ] . pll = DEFAULT_TOD1_PTP_PLL ;
idtcm - > channel [ 2 ] . pll = DEFAULT_TOD2_PTP_PLL ;
idtcm - > channel [ 3 ] . pll = DEFAULT_TOD3_PTP_PLL ;
2019-10-31 23:20:07 -04:00
idtcm - > channel [ 0 ] . output_mask = DEFAULT_OUTPUT_MASK_PLL0 ;
idtcm - > channel [ 1 ] . output_mask = DEFAULT_OUTPUT_MASK_PLL1 ;
idtcm - > channel [ 2 ] . output_mask = DEFAULT_OUTPUT_MASK_PLL2 ;
idtcm - > channel [ 3 ] . output_mask = DEFAULT_OUTPUT_MASK_PLL3 ;
}
2021-09-24 15:01:32 -04:00
static int idtcm_probe ( struct platform_device * pdev )
2019-10-31 23:20:07 -04:00
{
2021-09-24 15:01:32 -04:00
struct rsmu_ddata * ddata = dev_get_drvdata ( pdev - > dev . parent ) ;
2019-10-31 23:20:07 -04:00
struct idtcm * idtcm ;
int err ;
u8 i ;
2021-09-24 15:01:32 -04:00
idtcm = devm_kzalloc ( & pdev - > dev , sizeof ( struct idtcm ) , GFP_KERNEL ) ;
2019-10-31 23:20:07 -04:00
if ( ! idtcm )
return - ENOMEM ;
2021-09-24 15:01:32 -04:00
idtcm - > dev = & pdev - > dev ;
idtcm - > mfd = pdev - > dev . parent ;
idtcm - > lock = & ddata - > lock ;
idtcm - > regmap = ddata - > regmap ;
2019-10-31 23:20:07 -04:00
idtcm - > calculate_overhead_flag = 0 ;
2021-09-24 15:01:32 -04:00
INIT_DELAYED_WORK ( & idtcm - > extts_work , idtcm_extts_check ) ;
2019-10-31 23:20:07 -04:00
set_default_masks ( idtcm ) ;
2021-09-24 15:01:32 -04:00
mutex_lock ( idtcm - > lock ) ;
2019-10-31 23:20:07 -04:00
2020-12-08 10:41:57 -05:00
idtcm_set_version_info ( idtcm ) ;
2019-10-31 23:20:07 -04:00
2021-09-24 15:01:32 -04:00
err = idtcm_load_firmware ( idtcm , & pdev - > dev ) ;
2019-10-31 23:20:07 -04:00
if ( err )
2021-09-24 15:01:32 -04:00
dev_warn ( idtcm - > dev , " loading firmware failed with %d " , err ) ;
2019-10-31 23:20:07 -04:00
2021-02-17 00:42:12 -05:00
wait_for_chip_ready ( idtcm ) ;
2020-12-08 10:41:54 -05:00
2020-07-28 16:00:30 -04:00
if ( idtcm - > tod_mask ) {
for ( i = 0 ; i < MAX_TOD ; i + + ) {
2021-09-24 15:01:32 -04:00
if ( idtcm - > tod_mask & ( 1 < < i ) )
2019-10-31 23:20:07 -04:00
err = idtcm_enable_channel ( idtcm , i ) ;
2021-09-24 15:01:32 -04:00
else
err = idtcm_enable_extts_channel ( idtcm , i ) ;
if ( err ) {
dev_err ( idtcm - > dev ,
" idtcm_enable_channel %d failed! " , i ) ;
break ;
2019-10-31 23:20:07 -04:00
}
}
} else {
2021-09-24 15:01:32 -04:00
dev_err ( idtcm - > dev ,
2021-02-17 00:42:15 -05:00
" no PLLs flagged as PHCs, nothing to do " ) ;
2019-10-31 23:20:07 -04:00
err = - ENODEV ;
}
2021-09-24 15:01:32 -04:00
mutex_unlock ( idtcm - > lock ) ;
2019-10-31 23:20:07 -04:00
if ( err ) {
ptp_clock_unregister_all ( idtcm ) ;
return err ;
}
2021-09-24 15:01:32 -04:00
platform_set_drvdata ( pdev , idtcm ) ;
2019-10-31 23:20:07 -04:00
return 0 ;
}
2024-04-10 09:34:50 +02:00
static void idtcm_remove ( struct platform_device * pdev )
2019-10-31 23:20:07 -04:00
{
2021-09-24 15:01:32 -04:00
struct idtcm * idtcm = platform_get_drvdata ( pdev ) ;
2019-10-31 23:20:07 -04:00
2022-05-16 10:47:06 -04:00
idtcm - > extts_mask = 0 ;
2019-10-31 23:20:07 -04:00
ptp_clock_unregister_all ( idtcm ) ;
2021-09-24 15:01:32 -04:00
cancel_delayed_work_sync ( & idtcm - > extts_work ) ;
2019-10-31 23:20:07 -04:00
}
2021-09-24 15:01:32 -04:00
static struct platform_driver idtcm_driver = {
2019-10-31 23:20:07 -04:00
. driver = {
2021-09-24 15:01:32 -04:00
. name = " 8a3400x-phc " ,
2019-10-31 23:20:07 -04:00
} ,
2021-09-24 15:01:32 -04:00
. probe = idtcm_probe ,
2024-04-10 09:34:50 +02:00
. remove_new = idtcm_remove ,
2019-10-31 23:20:07 -04:00
} ;
2021-09-24 15:01:32 -04:00
module_platform_driver ( idtcm_driver ) ;