diff options
Diffstat (limited to 'drivers/ptp')
-rw-r--r-- | drivers/ptp/idt8a340_reg.h | 720 | ||||
-rw-r--r-- | drivers/ptp/ptp_clock.c | 6 | ||||
-rw-r--r-- | drivers/ptp/ptp_clockmatrix.c | 1447 | ||||
-rw-r--r-- | drivers/ptp/ptp_clockmatrix.h | 109 | ||||
-rw-r--r-- | drivers/ptp/ptp_ocp.c | 1325 |
5 files changed, 1999 insertions, 1608 deletions
diff --git a/drivers/ptp/idt8a340_reg.h b/drivers/ptp/idt8a340_reg.h deleted file mode 100644 index ac524cf0f31f..000000000000 --- a/drivers/ptp/idt8a340_reg.h +++ /dev/null @@ -1,720 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* idt8a340_reg.h - * - * Originally generated by regen.tcl on Thu Feb 14 19:23:44 PST 2019 - * https://github.com/richardcochran/regen - * - * Hand modified to include some HW registers. - * Based on 4.8.0, SCSR rev C commit a03c7ae5 - */ -#ifndef HAVE_IDT8A340_REG -#define HAVE_IDT8A340_REG - -#define PAGE_ADDR_BASE 0x0000 -#define PAGE_ADDR 0x00fc - -#define HW_REVISION 0x8180 -#define REV_ID 0x007a - -#define HW_DPLL_0 (0x8a00) -#define HW_DPLL_1 (0x8b00) -#define HW_DPLL_2 (0x8c00) -#define HW_DPLL_3 (0x8d00) -#define HW_DPLL_4 (0x8e00) -#define HW_DPLL_5 (0x8f00) -#define HW_DPLL_6 (0x9000) -#define HW_DPLL_7 (0x9100) - -#define HW_DPLL_TOD_SW_TRIG_ADDR__0 (0x080) -#define HW_DPLL_TOD_CTRL_1 (0x089) -#define HW_DPLL_TOD_CTRL_2 (0x08A) -#define HW_DPLL_TOD_OVR__0 (0x098) -#define HW_DPLL_TOD_OUT_0__0 (0x0B0) - -#define HW_Q0_Q1_CH_SYNC_CTRL_0 (0xa740) -#define HW_Q0_Q1_CH_SYNC_CTRL_1 (0xa741) -#define HW_Q2_Q3_CH_SYNC_CTRL_0 (0xa742) -#define HW_Q2_Q3_CH_SYNC_CTRL_1 (0xa743) -#define HW_Q4_Q5_CH_SYNC_CTRL_0 (0xa744) -#define HW_Q4_Q5_CH_SYNC_CTRL_1 (0xa745) -#define HW_Q6_Q7_CH_SYNC_CTRL_0 (0xa746) -#define HW_Q6_Q7_CH_SYNC_CTRL_1 (0xa747) -#define HW_Q8_CH_SYNC_CTRL_0 (0xa748) -#define HW_Q8_CH_SYNC_CTRL_1 (0xa749) -#define HW_Q9_CH_SYNC_CTRL_0 (0xa74a) -#define HW_Q9_CH_SYNC_CTRL_1 (0xa74b) -#define HW_Q10_CH_SYNC_CTRL_0 (0xa74c) -#define HW_Q10_CH_SYNC_CTRL_1 (0xa74d) -#define HW_Q11_CH_SYNC_CTRL_0 (0xa74e) -#define HW_Q11_CH_SYNC_CTRL_1 (0xa74f) - -#define SYNC_SOURCE_DPLL0_TOD_PPS 0x14 -#define SYNC_SOURCE_DPLL1_TOD_PPS 0x15 -#define SYNC_SOURCE_DPLL2_TOD_PPS 0x16 -#define SYNC_SOURCE_DPLL3_TOD_PPS 0x17 - -#define SYNCTRL1_MASTER_SYNC_RST BIT(7) -#define SYNCTRL1_MASTER_SYNC_TRIG BIT(5) -#define SYNCTRL1_TOD_SYNC_TRIG BIT(4) -#define SYNCTRL1_FBDIV_FRAME_SYNC_TRIG BIT(3) -#define SYNCTRL1_FBDIV_SYNC_TRIG BIT(2) -#define SYNCTRL1_Q1_DIV_SYNC_TRIG BIT(1) -#define SYNCTRL1_Q0_DIV_SYNC_TRIG BIT(0) - -#define HW_Q8_CTRL_SPARE (0xa7d4) -#define HW_Q11_CTRL_SPARE (0xa7ec) - -/** - * Select FOD5 as sync_trigger for Q8 divider. - * Transition from logic zero to one - * sets trigger to sync Q8 divider. - * - * Unused when FOD4 is driving Q8 divider (normal operation). - */ -#define Q9_TO_Q8_SYNC_TRIG BIT(1) - -/** - * Enable FOD5 as driver for clock and sync for Q8 divider. - * Enable fanout buffer for FOD5. - * - * Unused when FOD4 is driving Q8 divider (normal operation). - */ -#define Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK (BIT(0) | BIT(2)) - -/** - * Select FOD6 as sync_trigger for Q11 divider. - * Transition from logic zero to one - * sets trigger to sync Q11 divider. - * - * Unused when FOD7 is driving Q11 divider (normal operation). - */ -#define Q10_TO_Q11_SYNC_TRIG BIT(1) - -/** - * Enable FOD6 as driver for clock and sync for Q11 divider. - * Enable fanout buffer for FOD6. - * - * Unused when FOD7 is driving Q11 divider (normal operation). - */ -#define Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK (BIT(0) | BIT(2)) - -#define RESET_CTRL 0xc000 -#define SM_RESET 0x0012 -#define SM_RESET_CMD 0x5A - -#define GENERAL_STATUS 0xc014 -#define BOOT_STATUS 0x0000 -#define HW_REV_ID 0x000A -#define BOND_ID 0x000B -#define HW_CSR_ID 0x000C -#define HW_IRQ_ID 0x000E - -#define MAJ_REL 0x0010 -#define MIN_REL 0x0011 -#define HOTFIX_REL 0x0012 - -#define PIPELINE_ID 0x0014 -#define BUILD_ID 0x0018 - -#define JTAG_DEVICE_ID 0x001c -#define PRODUCT_ID 0x001e - -#define OTP_SCSR_CONFIG_SELECT 0x0022 - -#define STATUS 0xc03c -#define DPLL_SYS_STATUS 0x0020 -#define DPLL_SYS_APLL_STATUS 0x0021 -#define USER_GPIO0_TO_7_STATUS 0x008a -#define USER_GPIO8_TO_15_STATUS 0x008b - -#define GPIO_USER_CONTROL 0xc160 -#define GPIO0_TO_7_OUT 0x0000 -#define GPIO8_TO_15_OUT 0x0001 - -#define STICKY_STATUS_CLEAR 0xc164 - -#define GPIO_TOD_NOTIFICATION_CLEAR 0xc16c - -#define ALERT_CFG 0xc188 - -#define SYS_DPLL_XO 0xc194 - -#define SYS_APLL 0xc19c - -#define INPUT_0 0xc1b0 - -#define INPUT_1 0xc1c0 - -#define INPUT_2 0xc1d0 - -#define INPUT_3 0xc200 - -#define INPUT_4 0xc210 - -#define INPUT_5 0xc220 - -#define INPUT_6 0xc230 - -#define INPUT_7 0xc240 - -#define INPUT_8 0xc250 - -#define INPUT_9 0xc260 - -#define INPUT_10 0xc280 - -#define INPUT_11 0xc290 - -#define INPUT_12 0xc2a0 - -#define INPUT_13 0xc2b0 - -#define INPUT_14 0xc2c0 - -#define INPUT_15 0xc2d0 - -#define REF_MON_0 0xc2e0 - -#define REF_MON_1 0xc2ec - -#define REF_MON_2 0xc300 - -#define REF_MON_3 0xc30c - -#define REF_MON_4 0xc318 - -#define REF_MON_5 0xc324 - -#define REF_MON_6 0xc330 - -#define REF_MON_7 0xc33c - -#define REF_MON_8 0xc348 - -#define REF_MON_9 0xc354 - -#define REF_MON_10 0xc360 - -#define REF_MON_11 0xc36c - -#define REF_MON_12 0xc380 - -#define REF_MON_13 0xc38c - -#define REF_MON_14 0xc398 - -#define REF_MON_15 0xc3a4 - -#define DPLL_0 0xc3b0 -#define DPLL_CTRL_REG_0 0x0002 -#define DPLL_CTRL_REG_1 0x0003 -#define DPLL_CTRL_REG_2 0x0004 -#define DPLL_TOD_SYNC_CFG 0x0031 -#define DPLL_COMBO_SLAVE_CFG_0 0x0032 -#define DPLL_COMBO_SLAVE_CFG_1 0x0033 -#define DPLL_SLAVE_REF_CFG 0x0034 -#define DPLL_REF_MODE 0x0035 -#define DPLL_PHASE_MEASUREMENT_CFG 0x0036 -#define DPLL_MODE 0x0037 - -#define DPLL_1 0xc400 - -#define DPLL_2 0xc438 - -#define DPLL_3 0xc480 - -#define DPLL_4 0xc4b8 - -#define DPLL_5 0xc500 - -#define DPLL_6 0xc538 - -#define DPLL_7 0xc580 - -#define SYS_DPLL 0xc5b8 - -#define DPLL_CTRL_0 0xc600 -#define DPLL_CTRL_DPLL_MANU_REF_CFG 0x0001 -#define DPLL_CTRL_COMBO_MASTER_CFG 0x003a - -#define DPLL_CTRL_1 0xc63c - -#define DPLL_CTRL_2 0xc680 - -#define DPLL_CTRL_3 0xc6bc - -#define DPLL_CTRL_4 0xc700 - -#define DPLL_CTRL_5 0xc73c - -#define DPLL_CTRL_6 0xc780 - -#define DPLL_CTRL_7 0xc7bc - -#define SYS_DPLL_CTRL 0xc800 - -#define DPLL_PHASE_0 0xc818 - -/* Signed 42-bit FFO in units of 2^(-53) */ -#define DPLL_WR_PHASE 0x0000 - -#define DPLL_PHASE_1 0xc81c - -#define DPLL_PHASE_2 0xc820 - -#define DPLL_PHASE_3 0xc824 - -#define DPLL_PHASE_4 0xc828 - -#define DPLL_PHASE_5 0xc82c - -#define DPLL_PHASE_6 0xc830 - -#define DPLL_PHASE_7 0xc834 - -#define DPLL_FREQ_0 0xc838 - -/* Signed 42-bit FFO in units of 2^(-53) */ -#define DPLL_WR_FREQ 0x0000 - -#define DPLL_FREQ_1 0xc840 - -#define DPLL_FREQ_2 0xc848 - -#define DPLL_FREQ_3 0xc850 - -#define DPLL_FREQ_4 0xc858 - -#define DPLL_FREQ_5 0xc860 - -#define DPLL_FREQ_6 0xc868 - -#define DPLL_FREQ_7 0xc870 - -#define DPLL_PHASE_PULL_IN_0 0xc880 -#define PULL_IN_OFFSET 0x0000 /* Signed 32 bit */ -#define PULL_IN_SLOPE_LIMIT 0x0004 /* Unsigned 24 bit */ -#define PULL_IN_CTRL 0x0007 - -#define DPLL_PHASE_PULL_IN_1 0xc888 - -#define DPLL_PHASE_PULL_IN_2 0xc890 - -#define DPLL_PHASE_PULL_IN_3 0xc898 - -#define DPLL_PHASE_PULL_IN_4 0xc8a0 - -#define DPLL_PHASE_PULL_IN_5 0xc8a8 - -#define DPLL_PHASE_PULL_IN_6 0xc8b0 - -#define DPLL_PHASE_PULL_IN_7 0xc8b8 - -#define GPIO_CFG 0xc8c0 -#define GPIO_CFG_GBL 0x0000 - -#define GPIO_0 0xc8c2 -#define GPIO_DCO_INC_DEC 0x0000 -#define GPIO_OUT_CTRL_0 0x0001 -#define GPIO_OUT_CTRL_1 0x0002 -#define GPIO_TOD_TRIG 0x0003 -#define GPIO_DPLL_INDICATOR 0x0004 -#define GPIO_LOS_INDICATOR 0x0005 -#define GPIO_REF_INPUT_DSQ_0 0x0006 -#define GPIO_REF_INPUT_DSQ_1 0x0007 -#define GPIO_REF_INPUT_DSQ_2 0x0008 -#define GPIO_REF_INPUT_DSQ_3 0x0009 -#define GPIO_MAN_CLK_SEL_0 0x000a -#define GPIO_MAN_CLK_SEL_1 0x000b -#define GPIO_MAN_CLK_SEL_2 0x000c -#define GPIO_SLAVE 0x000d -#define GPIO_ALERT_OUT_CFG 0x000e -#define GPIO_TOD_NOTIFICATION_CFG 0x000f -#define GPIO_CTRL 0x0010 - -#define GPIO_1 0xc8d4 - -#define GPIO_2 0xc8e6 - -#define GPIO_3 0xc900 - -#define GPIO_4 0xc912 - -#define GPIO_5 0xc924 - -#define GPIO_6 0xc936 - -#define GPIO_7 0xc948 - -#define GPIO_8 0xc95a - -#define GPIO_9 0xc980 - -#define GPIO_10 0xc992 - -#define GPIO_11 0xc9a4 - -#define GPIO_12 0xc9b6 - -#define GPIO_13 0xc9c8 - -#define GPIO_14 0xc9da - -#define GPIO_15 0xca00 - -#define OUT_DIV_MUX 0xca12 - -#define OUTPUT_0 0xca14 -/* FOD frequency output divider value */ -#define OUT_DIV 0x0000 -#define OUT_DUTY_CYCLE_HIGH 0x0004 -#define OUT_CTRL_0 0x0008 -#define OUT_CTRL_1 0x0009 -/* Phase adjustment in FOD cycles */ -#define OUT_PHASE_ADJ 0x000c - -#define OUTPUT_1 0xca24 - -#define OUTPUT_2 0xca34 - -#define OUTPUT_3 0xca44 - -#define OUTPUT_4 0xca54 - -#define OUTPUT_5 0xca64 - -#define OUTPUT_6 0xca80 - -#define OUTPUT_7 0xca90 - -#define OUTPUT_8 0xcaa0 - -#define OUTPUT_9 0xcab0 - -#define OUTPUT_10 0xcac0 - -#define OUTPUT_11 0xcad0 - -#define SERIAL 0xcae0 - -#define PWM_ENCODER_0 0xcb00 - -#define PWM_ENCODER_1 0xcb08 - -#define PWM_ENCODER_2 0xcb10 - -#define PWM_ENCODER_3 0xcb18 - -#define PWM_ENCODER_4 0xcb20 - -#define PWM_ENCODER_5 0xcb28 - -#define PWM_ENCODER_6 0xcb30 - -#define PWM_ENCODER_7 0xcb38 - -#define PWM_DECODER_0 0xcb40 - -#define PWM_DECODER_1 0xcb48 - -#define PWM_DECODER_2 0xcb50 - -#define PWM_DECODER_3 0xcb58 - -#define PWM_DECODER_4 0xcb60 - -#define PWM_DECODER_5 0xcb68 - -#define PWM_DECODER_6 0xcb70 - -#define PWM_DECODER_7 0xcb80 - -#define PWM_DECODER_8 0xcb88 - -#define PWM_DECODER_9 0xcb90 - -#define PWM_DECODER_10 0xcb98 - -#define PWM_DECODER_11 0xcba0 - -#define PWM_DECODER_12 0xcba8 - -#define PWM_DECODER_13 0xcbb0 - -#define PWM_DECODER_14 0xcbb8 - -#define PWM_DECODER_15 0xcbc0 - -#define PWM_USER_DATA 0xcbc8 - -#define TOD_0 0xcbcc - -/* Enable TOD counter, output channel sync and even-PPS mode */ -#define TOD_CFG 0x0000 - -#define TOD_1 0xcbce - -#define TOD_2 0xcbd0 - -#define TOD_3 0xcbd2 - - -#define TOD_WRITE_0 0xcc00 -/* 8-bit subns, 32-bit ns, 48-bit seconds */ -#define TOD_WRITE 0x0000 -/* Counter increments after TOD write is completed */ -#define TOD_WRITE_COUNTER 0x000c -/* TOD write trigger configuration */ -#define TOD_WRITE_SELECT_CFG_0 0x000d -/* TOD write trigger selection */ -#define TOD_WRITE_CMD 0x000f - -#define TOD_WRITE_1 0xcc10 - -#define TOD_WRITE_2 0xcc20 - -#define TOD_WRITE_3 0xcc30 - -#define TOD_READ_PRIMARY_0 0xcc40 -/* 8-bit subns, 32-bit ns, 48-bit seconds */ -#define TOD_READ_PRIMARY 0x0000 -/* Counter increments after TOD write is completed */ -#define TOD_READ_PRIMARY_COUNTER 0x000b -/* Read trigger configuration */ -#define TOD_READ_PRIMARY_SEL_CFG_0 0x000c -/* Read trigger selection */ -#define TOD_READ_PRIMARY_CMD 0x000e - -#define TOD_READ_PRIMARY_1 0xcc50 - -#define TOD_READ_PRIMARY_2 0xcc60 - -#define TOD_READ_PRIMARY_3 0xcc80 - -#define TOD_READ_SECONDARY_0 0xcc90 - -#define TOD_READ_SECONDARY_1 0xcca0 - -#define TOD_READ_SECONDARY_2 0xccb0 - -#define TOD_READ_SECONDARY_3 0xccc0 - -#define OUTPUT_TDC_CFG 0xccd0 - -#define OUTPUT_TDC_0 0xcd00 - -#define OUTPUT_TDC_1 0xcd08 - -#define OUTPUT_TDC_2 0xcd10 - -#define OUTPUT_TDC_3 0xcd18 - -#define INPUT_TDC 0xcd20 - -#define SCRATCH 0xcf50 - -#define EEPROM 0xcf68 - -#define OTP 0xcf70 - -#define BYTE 0xcf80 - -/* Bit definitions for the MAJ_REL register */ -#define MAJOR_SHIFT (1) -#define MAJOR_MASK (0x7f) -#define PR_BUILD BIT(0) - -/* Bit definitions for the USER_GPIO0_TO_7_STATUS register */ -#define GPIO0_LEVEL BIT(0) -#define GPIO1_LEVEL BIT(1) -#define GPIO2_LEVEL BIT(2) -#define GPIO3_LEVEL BIT(3) -#define GPIO4_LEVEL BIT(4) -#define GPIO5_LEVEL BIT(5) -#define GPIO6_LEVEL BIT(6) -#define GPIO7_LEVEL BIT(7) - -/* Bit definitions for the USER_GPIO8_TO_15_STATUS register */ -#define GPIO8_LEVEL BIT(0) -#define GPIO9_LEVEL BIT(1) -#define GPIO10_LEVEL BIT(2) -#define GPIO11_LEVEL BIT(3) -#define GPIO12_LEVEL BIT(4) -#define GPIO13_LEVEL BIT(5) -#define GPIO14_LEVEL BIT(6) -#define GPIO15_LEVEL BIT(7) - -/* Bit definitions for the GPIO0_TO_7_OUT register */ -#define GPIO0_DRIVE_LEVEL BIT(0) -#define GPIO1_DRIVE_LEVEL BIT(1) -#define GPIO2_DRIVE_LEVEL BIT(2) -#define GPIO3_DRIVE_LEVEL BIT(3) -#define GPIO4_DRIVE_LEVEL BIT(4) -#define GPIO5_DRIVE_LEVEL BIT(5) -#define GPIO6_DRIVE_LEVEL BIT(6) -#define GPIO7_DRIVE_LEVEL BIT(7) - -/* Bit definitions for the GPIO8_TO_15_OUT register */ -#define GPIO8_DRIVE_LEVEL BIT(0) -#define GPIO9_DRIVE_LEVEL BIT(1) -#define GPIO10_DRIVE_LEVEL BIT(2) -#define GPIO11_DRIVE_LEVEL BIT(3) -#define GPIO12_DRIVE_LEVEL BIT(4) -#define GPIO13_DRIVE_LEVEL BIT(5) -#define GPIO14_DRIVE_LEVEL BIT(6) -#define GPIO15_DRIVE_LEVEL BIT(7) - -/* Bit definitions for the DPLL_TOD_SYNC_CFG register */ -#define TOD_SYNC_SOURCE_SHIFT (1) -#define TOD_SYNC_SOURCE_MASK (0x3) -#define TOD_SYNC_EN BIT(0) - -/* Bit definitions for the DPLL_MODE register */ -#define WRITE_TIMER_MODE BIT(6) -#define PLL_MODE_SHIFT (3) -#define PLL_MODE_MASK (0x7) -#define STATE_MODE_SHIFT (0) -#define STATE_MODE_MASK (0x7) - -/* Bit definitions for the GPIO_CFG_GBL register */ -#define SUPPLY_MODE_SHIFT (0) -#define SUPPLY_MODE_MASK (0x3) - -/* Bit definitions for the GPIO_DCO_INC_DEC register */ -#define INCDEC_DPLL_INDEX_SHIFT (0) -#define INCDEC_DPLL_INDEX_MASK (0x7) - -/* Bit definitions for the GPIO_OUT_CTRL_0 register */ -#define CTRL_OUT_0 BIT(0) -#define CTRL_OUT_1 BIT(1) -#define CTRL_OUT_2 BIT(2) -#define CTRL_OUT_3 BIT(3) -#define CTRL_OUT_4 BIT(4) -#define CTRL_OUT_5 BIT(5) -#define CTRL_OUT_6 BIT(6) -#define CTRL_OUT_7 BIT(7) - -/* Bit definitions for the GPIO_OUT_CTRL_1 register */ -#define CTRL_OUT_8 BIT(0) -#define CTRL_OUT_9 BIT(1) -#define CTRL_OUT_10 BIT(2) -#define CTRL_OUT_11 BIT(3) -#define CTRL_OUT_12 BIT(4) -#define CTRL_OUT_13 BIT(5) -#define CTRL_OUT_14 BIT(6) -#define CTRL_OUT_15 BIT(7) - -/* Bit definitions for the GPIO_TOD_TRIG register */ -#define TOD_TRIG_0 BIT(0) -#define TOD_TRIG_1 BIT(1) -#define TOD_TRIG_2 BIT(2) -#define TOD_TRIG_3 BIT(3) - -/* Bit definitions for the GPIO_DPLL_INDICATOR register */ -#define IND_DPLL_INDEX_SHIFT (0) -#define IND_DPLL_INDEX_MASK (0x7) - -/* Bit definitions for the GPIO_LOS_INDICATOR register */ -#define REFMON_INDEX_SHIFT (0) -#define REFMON_INDEX_MASK (0xf) -/* Active level of LOS indicator, 0=low 1=high */ -#define ACTIVE_LEVEL BIT(4) - -/* Bit definitions for the GPIO_REF_INPUT_DSQ_0 register */ -#define DSQ_INP_0 BIT(0) -#define DSQ_INP_1 BIT(1) -#define DSQ_INP_2 BIT(2) -#define DSQ_INP_3 BIT(3) -#define DSQ_INP_4 BIT(4) -#define DSQ_INP_5 BIT(5) -#define DSQ_INP_6 BIT(6) -#define DSQ_INP_7 BIT(7) - -/* Bit definitions for the GPIO_REF_INPUT_DSQ_1 register */ -#define DSQ_INP_8 BIT(0) -#define DSQ_INP_9 BIT(1) -#define DSQ_INP_10 BIT(2) -#define DSQ_INP_11 BIT(3) -#define DSQ_INP_12 BIT(4) -#define DSQ_INP_13 BIT(5) -#define DSQ_INP_14 BIT(6) -#define DSQ_INP_15 BIT(7) - -/* Bit definitions for the GPIO_REF_INPUT_DSQ_2 register */ -#define DSQ_DPLL_0 BIT(0) -#define DSQ_DPLL_1 BIT(1) -#define DSQ_DPLL_2 BIT(2) -#define DSQ_DPLL_3 BIT(3) -#define DSQ_DPLL_4 BIT(4) -#define DSQ_DPLL_5 BIT(5) -#define DSQ_DPLL_6 BIT(6) -#define DSQ_DPLL_7 BIT(7) - -/* Bit definitions for the GPIO_REF_INPUT_DSQ_3 register */ -#define DSQ_DPLL_SYS BIT(0) -#define GPIO_DSQ_LEVEL BIT(1) - -/* Bit definitions for the GPIO_TOD_NOTIFICATION_CFG register */ -#define DPLL_TOD_SHIFT (0) -#define DPLL_TOD_MASK (0x3) -#define TOD_READ_SECONDARY BIT(2) -#define GPIO_ASSERT_LEVEL BIT(3) - -/* Bit definitions for the GPIO_CTRL register */ -#define GPIO_FUNCTION_EN BIT(0) -#define GPIO_CMOS_OD_MODE BIT(1) -#define GPIO_CONTROL_DIR BIT(2) -#define GPIO_PU_PD_MODE BIT(3) -#define GPIO_FUNCTION_SHIFT (4) -#define GPIO_FUNCTION_MASK (0xf) - -/* Bit definitions for the OUT_CTRL_1 register */ -#define OUT_SYNC_DISABLE BIT(7) -#define SQUELCH_VALUE BIT(6) -#define SQUELCH_DISABLE BIT(5) -#define PAD_VDDO_SHIFT (2) -#define PAD_VDDO_MASK (0x7) -#define PAD_CMOSDRV_SHIFT (0) -#define PAD_CMOSDRV_MASK (0x3) - -/* Bit definitions for the TOD_CFG register */ -#define TOD_EVEN_PPS_MODE BIT(2) -#define TOD_OUT_SYNC_ENABLE BIT(1) -#define TOD_ENABLE BIT(0) - -/* Bit definitions for the TOD_WRITE_SELECT_CFG_0 register */ -#define WR_PWM_DECODER_INDEX_SHIFT (4) -#define WR_PWM_DECODER_INDEX_MASK (0xf) -#define WR_REF_INDEX_SHIFT (0) -#define WR_REF_INDEX_MASK (0xf) - -/* Bit definitions for the TOD_WRITE_CMD register */ -#define TOD_WRITE_SELECTION_SHIFT (0) -#define TOD_WRITE_SELECTION_MASK (0xf) -/* 4.8.7 */ -#define TOD_WRITE_TYPE_SHIFT (4) -#define TOD_WRITE_TYPE_MASK (0x3) - -/* Bit definitions for the TOD_READ_PRIMARY_SEL_CFG_0 register */ -#define RD_PWM_DECODER_INDEX_SHIFT (4) -#define RD_PWM_DECODER_INDEX_MASK (0xf) -#define RD_REF_INDEX_SHIFT (0) -#define RD_REF_INDEX_MASK (0xf) - -/* Bit definitions for the TOD_READ_PRIMARY_CMD register */ -#define TOD_READ_TRIGGER_MODE BIT(4) -#define TOD_READ_TRIGGER_SHIFT (0) -#define TOD_READ_TRIGGER_MASK (0xf) - -/* Bit definitions for the DPLL_CTRL_COMBO_MASTER_CFG register */ -#define COMBO_MASTER_HOLD BIT(0) - -/* Bit definitions for DPLL_SYS_STATUS register */ -#define DPLL_SYS_STATE_MASK (0xf) - -/* Bit definitions for SYS_APLL_STATUS register */ -#define SYS_APLL_LOSS_LOCK_LIVE_MASK BIT(0) -#define SYS_APLL_LOSS_LOCK_LIVE_LOCKED 0 -#define SYS_APLL_LOSS_LOCK_LIVE_UNLOCKED 1 - -#endif diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index f9b2d66b0443..0e4bc8b9329d 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -284,11 +284,11 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, /* Create a posix clock and link it to the device. */ err = posix_clock_register(&ptp->clock, &ptp->dev); if (err) { - if (ptp->pps_source) - pps_unregister_source(ptp->pps_source); + if (ptp->pps_source) + pps_unregister_source(ptp->pps_source); if (ptp->kworker) - kthread_destroy_worker(ptp->kworker); + kthread_destroy_worker(ptp->kworker); put_device(&ptp->dev); diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c index fa636951169e..08e429a06922 100644 --- a/drivers/ptp/ptp_clockmatrix.c +++ b/drivers/ptp/ptp_clockmatrix.c @@ -6,7 +6,7 @@ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company. */ #include <linux/firmware.h> -#include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/module.h> #include <linux/ptp_clock_kernel.h> #include <linux/delay.h> @@ -14,6 +14,10 @@ #include <linux/kernel.h> #include <linux/timekeeping.h> #include <linux/string.h> +#include <linux/of.h> +#include <linux/mfd/rsmu.h> +#include <linux/mfd/idt8a340_reg.h> +#include <asm/unaligned.h> #include "ptp_private.h" #include "ptp_clockmatrix.h" @@ -32,16 +36,43 @@ static char *firmware; module_param(firmware, charp, 0); #define SETTIME_CORRECTION (0) +#define EXTTS_PERIOD_MS (95) -static int contains_full_configuration(const struct firmware *fw) +static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm); + +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); +} + +static int contains_full_configuration(struct idtcm *idtcm, + const struct firmware *fw) { - s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES; struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data; + u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH); + s32 full_count; s32 count = 0; u16 regaddr; u8 loaddr; s32 len; + /* 4 bytes skipped every 0x80 */ + full_count = (scratch - GPIO_USER_CONTROL) - + ((scratch >> 7) - (GPIO_USER_CONTROL >> 7)) * 4; + /* If the firmware contains 'full configuration' SM_RESET can be used * to ensure proper configuration. * @@ -57,7 +88,7 @@ static int contains_full_configuration(const struct firmware *fw) rec++; /* Top (status registers) and bottom are read-only */ - if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH) + if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch) continue; /* Page size 128, last 4 bytes of page skipped */ @@ -152,132 +183,17 @@ static int idtcm_strverscmp(const char *version1, const char *version2) return 0; } -static int idtcm_xfer_read(struct idtcm *idtcm, - u8 regaddr, - u8 *buf, - u16 count) -{ - struct i2c_client *client = idtcm->client; - struct i2c_msg msg[2]; - int cnt; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = ®addr; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = count; - msg[1].buf = buf; - - cnt = i2c_transfer(client->adapter, msg, 2); - - if (cnt < 0) { - dev_err(&client->dev, - "i2c_transfer failed at %d in %s, at addr: %04x!", - __LINE__, __func__, regaddr); - return cnt; - } else if (cnt != 2) { - dev_err(&client->dev, - "i2c_transfer sent only %d of %d messages", cnt, 2); - return -EIO; - } - - return 0; -} - -static int idtcm_xfer_write(struct idtcm *idtcm, - u8 regaddr, - u8 *buf, - u16 count) -{ - struct i2c_client *client = idtcm->client; - /* we add 1 byte for device register */ - u8 msg[IDTCM_MAX_WRITE_COUNT + 1]; - int cnt; - - if (count > IDTCM_MAX_WRITE_COUNT) - return -EINVAL; - - msg[0] = regaddr; - memcpy(&msg[1], buf, count); - - cnt = i2c_master_send(client, msg, count + 1); - - if (cnt < 0) { - dev_err(&client->dev, - "i2c_master_send failed at %d in %s, at addr: %04x!", - __LINE__, __func__, regaddr); - return cnt; - } - - return 0; -} - -static int idtcm_page_offset(struct idtcm *idtcm, u8 val) -{ - u8 buf[4]; - int err; - - if (idtcm->page_offset == val) - return 0; - - buf[0] = 0x0; - buf[1] = val; - buf[2] = 0x10; - buf[3] = 0x20; - - err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf)); - if (err) { - idtcm->page_offset = 0xff; - dev_err(&idtcm->client->dev, "failed to set page offset"); - } else { - idtcm->page_offset = val; - } - - return err; -} - -static int _idtcm_rdwr(struct idtcm *idtcm, - u16 regaddr, - u8 *buf, - u16 count, - bool write) +static enum fw_version idtcm_fw_version(const char *version) { - u8 hi; - u8 lo; - int err; + enum fw_version ver = V_DEFAULT; - hi = (regaddr >> 8) & 0xff; - lo = regaddr & 0xff; - - err = idtcm_page_offset(idtcm, hi); - if (err) - return err; - - if (write) - return idtcm_xfer_write(idtcm, lo, buf, count); - - return idtcm_xfer_read(idtcm, lo, buf, count); -} + if (idtcm_strverscmp(version, "4.8.7") >= 0) + ver = V487; -static int idtcm_read(struct idtcm *idtcm, - u16 module, - u16 regaddr, - u8 *buf, - u16 count) -{ - return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false); -} + if (idtcm_strverscmp(version, "5.2.0") >= 0) + ver = V520; -static int idtcm_write(struct idtcm *idtcm, - u16 module, - u16 regaddr, - u8 *buf, - u16 count) -{ - return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true); + return ver; } static int clear_boot_status(struct idtcm *idtcm) @@ -318,11 +234,82 @@ static int wait_for_boot_status_ready(struct idtcm *idtcm) } while (i); - dev_warn(&idtcm->client->dev, "%s timed out", __func__); + dev_warn(idtcm->dev, "%s timed out", __func__); return -EBUSY; } +static int _idtcm_set_scsr_read_trig(struct idtcm_channel *channel, + enum scsr_read_trig_sel trig, u8 ref) +{ + struct idtcm *idtcm = channel->idtcm; + u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD); + u8 val; + int err; + + if (trig == SCSR_TOD_READ_TRIG_SEL_REFCLK) { + err = idtcm_read(idtcm, channel->tod_read_primary, + TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val)); + if (err) + return err; + + val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT); + val |= (ref << WR_REF_INDEX_SHIFT); + + err = idtcm_write(idtcm, channel->tod_read_primary, + TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val)); + if (err) + return err; + } + + err = idtcm_read(idtcm, channel->tod_read_primary, + tod_read_cmd, &val, sizeof(val)); + if (err) + return err; + + val &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT); + val |= (trig << TOD_READ_TRIGGER_SHIFT); + val &= ~TOD_READ_TRIGGER_MODE; /* single shot */ + + err = idtcm_write(idtcm, channel->tod_read_primary, + tod_read_cmd, &val, sizeof(val)); + return err; +} + +static int idtcm_enable_extts(struct idtcm_channel *channel, u8 todn, u8 ref, + bool enable) +{ + struct idtcm *idtcm = channel->idtcm; + u8 old_mask = idtcm->extts_mask; + u8 mask = 1 << todn; + int err = 0; + + if (todn >= MAX_TOD) + return -EINVAL; + + if (enable) { + if (ref > 0xF) /* E_REF_CLK15 */ + return -EINVAL; + if (idtcm->extts_mask & mask) + return 0; + err = _idtcm_set_scsr_read_trig(&idtcm->channel[todn], + SCSR_TOD_READ_TRIG_SEL_REFCLK, + ref); + if (err == 0) { + idtcm->extts_mask |= mask; + idtcm->event_channel[todn] = channel; + idtcm->channel[todn].refn = ref; + } + } else + idtcm->extts_mask &= ~mask; + + if (old_mask == 0 && idtcm->extts_mask) + schedule_delayed_work(&idtcm->extts_work, + msecs_to_jiffies(EXTTS_PERIOD_MS)); + + return err; +} + static int read_sys_apll_status(struct idtcm *idtcm, u8 *status) { return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status, @@ -359,7 +346,7 @@ static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) } else if (dpll == DPLL_STATE_FREERUN || dpll == DPLL_STATE_HOLDOVER || dpll == DPLL_STATE_OPEN_LOOP) { - dev_warn(&idtcm->client->dev, + dev_warn(idtcm->dev, "No wait state: DPLL_SYS_STATE %d", dpll); return -EPERM; } @@ -367,7 +354,7 @@ static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) msleep(LOCK_POLL_INTERVAL_MS); } while (time_is_after_jiffies(timeout)); - dev_warn(&idtcm->client->dev, + dev_warn(idtcm->dev, "%d ms lock timeout: SYS APLL Loss Lock %d SYS DPLL state %d", LOCK_TIMEOUT_MS, apll, dpll); @@ -377,50 +364,36 @@ static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) static void wait_for_chip_ready(struct idtcm *idtcm) { if (wait_for_boot_status_ready(idtcm)) - dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0"); + dev_warn(idtcm->dev, "BOOT_STATUS != 0xA0"); if (wait_for_sys_apll_dpll_lock(idtcm)) - dev_warn(&idtcm->client->dev, + dev_warn(idtcm->dev, "Continuing while SYS APLL/DPLL is not locked"); } static int _idtcm_gettime(struct idtcm_channel *channel, - struct timespec64 *ts) + struct timespec64 *ts, u8 timeout) { struct idtcm *idtcm = channel->idtcm; + u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD); u8 buf[TOD_BYTE_COUNT]; - u8 timeout = 10; u8 trigger; int err; - err = idtcm_read(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); - if (err) - return err; - - trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT); - trigger |= (1 << TOD_READ_TRIGGER_SHIFT); - trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */ - - err = idtcm_write(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); - if (err) - return err; - /* wait trigger to be 0 */ - while (trigger & TOD_READ_TRIGGER_MASK) { + do { + if (timeout-- == 0) + return -EIO; + if (idtcm->calculate_overhead_flag) idtcm->start_time = ktime_get_raw(); err = idtcm_read(idtcm, channel->tod_read_primary, - TOD_READ_PRIMARY_CMD, &trigger, + tod_read_cmd, &trigger, sizeof(trigger)); if (err) return err; - - if (--timeout == 0) - return -EIO; - } + } while (trigger & TOD_READ_TRIGGER_MASK); err = idtcm_read(idtcm, channel->tod_read_primary, TOD_READ_PRIMARY, buf, sizeof(buf)); @@ -432,6 +405,79 @@ static int _idtcm_gettime(struct idtcm_channel *channel, return err; } +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]; + if (extts_channel == ptp_channel) + dco_delay = ptp_channel->dco_delay; + + err = _idtcm_gettime(extts_channel, &ts, 1); + if (err == 0) { + event.type = PTP_CLOCK_EXTTS; + event.index = todn; + event.timestamp = timespec64_to_ns(&ts) - dco_delay; + ptp_clock_event(ptp_channel->ptp_clock, &event); + } + return err; +} + +static u8 idtcm_enable_extts_mask(struct idtcm_channel *channel, + u8 extts_mask, bool enable) +{ + struct idtcm *idtcm = channel->idtcm; + int i, err; + + for (i = 0; i < MAX_TOD; i++) { + u8 mask = 1 << i; + u8 refn = idtcm->channel[i].refn; + + if (extts_mask & mask) { + /* check extts before disabling it */ + if (enable == false) { + err = idtcm_extts_check_channel(idtcm, i); + /* trigger happened so we won't re-enable it */ + if (err == 0) + extts_mask &= ~mask; + } + (void)idtcm_enable_extts(channel, i, refn, enable); + } + } + + return extts_mask; +} + +static int _idtcm_gettime_immediate(struct idtcm_channel *channel, + struct timespec64 *ts) +{ + struct idtcm *idtcm = channel->idtcm; + u8 extts_mask = 0; + int err; + + /* Disable extts */ + if (idtcm->extts_mask) { + extts_mask = idtcm_enable_extts_mask(channel, idtcm->extts_mask, + false); + } + + err = _idtcm_set_scsr_read_trig(channel, + SCSR_TOD_READ_TRIG_SEL_IMMEDIATE, 0); + if (err == 0) + err = _idtcm_gettime(channel, ts, 10); + + /* Re-enable extts */ + if (extts_mask) + idtcm_enable_extts_mask(channel, extts_mask, true); + + return err; +} + static int _sync_pll_output(struct idtcm *idtcm, u8 pll, u8 sync_src, @@ -559,35 +605,10 @@ static int _sync_pll_output(struct idtcm *idtcm, return err; } -static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src) -{ - int err = 0; - - switch (tod_addr) { - case TOD_0: - *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; - break; - case TOD_1: - *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; - break; - case TOD_2: - *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; - break; - case TOD_3: - *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; - break; - default: - err = -EINVAL; - } - - return err; -} - static int idtcm_sync_pps_output(struct idtcm_channel *channel) { struct idtcm *idtcm = channel->idtcm; u8 pll; - u8 sync_src; u8 qn; u8 qn_plus_1; int err = 0; @@ -596,10 +617,6 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) u8 temp; u16 output_mask = channel->output_mask; - err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src); - if (err) - return err; - err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp)); if (err) @@ -655,8 +672,8 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) } if (qn != 0 || qn_plus_1 != 0) - err = _sync_pll_output(idtcm, pll, sync_src, qn, - qn_plus_1); + err = _sync_pll_output(idtcm, pll, channel->sync_src, + qn, qn_plus_1); if (err) return err; @@ -666,8 +683,8 @@ static int idtcm_sync_pps_output(struct idtcm_channel *channel) } static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel, - struct timespec64 const *ts, - enum hw_tod_write_trig_sel wr_trig) + struct timespec64 const *ts, + enum hw_tod_write_trig_sel wr_trig) { struct idtcm *idtcm = channel->idtcm; u8 buf[TOD_BYTE_COUNT]; @@ -784,7 +801,7 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, break; if (++count > 20) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Timed out waiting for the write counter"); return -EIO; } @@ -793,46 +810,46 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, return 0; } -static int get_output_base_addr(u8 outn) +static int get_output_base_addr(enum fw_version ver, u8 outn) { int base; switch (outn) { case 0: - base = OUTPUT_0; + base = IDTCM_FW_REG(ver, V520, OUTPUT_0); break; case 1: - base = OUTPUT_1; + base = IDTCM_FW_REG(ver, V520, OUTPUT_1); break; case 2: - base = OUTPUT_2; + base = IDTCM_FW_REG(ver, V520, OUTPUT_2); break; case 3: - base = OUTPUT_3; + base = IDTCM_FW_REG(ver, V520, OUTPUT_3); break; case 4: - base = OUTPUT_4; + base = IDTCM_FW_REG(ver, V520, OUTPUT_4); break; case 5: - base = OUTPUT_5; + base = IDTCM_FW_REG(ver, V520, OUTPUT_5); break; case 6: - base = OUTPUT_6; + base = IDTCM_FW_REG(ver, V520, OUTPUT_6); break; case 7: - base = OUTPUT_7; + base = IDTCM_FW_REG(ver, V520, OUTPUT_7); break; case 8: - base = OUTPUT_8; + base = IDTCM_FW_REG(ver, V520, OUTPUT_8); break; case 9: - base = OUTPUT_9; + base = IDTCM_FW_REG(ver, V520, OUTPUT_9); break; case 10: - base = OUTPUT_10; + base = IDTCM_FW_REG(ver, V520, OUTPUT_10); break; case 11: - base = OUTPUT_11; + base = IDTCM_FW_REG(ver, V520, OUTPUT_11); break; default: base = -EINVAL; @@ -849,7 +866,7 @@ static int _idtcm_settime_deprecated(struct idtcm_channel *channel, err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB); if (err) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "%s: Set HW ToD failed", __func__); return err; } @@ -929,9 +946,9 @@ static int idtcm_start_phase_pull_in(struct idtcm_channel *channel) return err; } -static int idtcm_do_phase_pull_in(struct idtcm_channel *channel, - s32 offset_ns, - u32 max_ffo_ppb) +static int do_phase_pull_in_fw(struct idtcm_channel *channel, + s32 offset_ns, + u32 max_ffo_ppb) { int err; @@ -1000,7 +1017,7 @@ static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) s64 now; if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) { - err = idtcm_do_phase_pull_in(channel, delta, 0); + err = channel->do_phase_pull_in(channel, delta, 0); } else { idtcm->calculate_overhead_flag = 1; @@ -1008,7 +1025,7 @@ static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) if (err) return err; - err = _idtcm_gettime(channel, &ts); + err = _idtcm_gettime_immediate(channel, &ts); if (err) return err; @@ -1032,7 +1049,9 @@ static int idtcm_state_machine_reset(struct idtcm *idtcm) clear_boot_status(idtcm); - err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte)); + err = idtcm_write(idtcm, RESET_CTRL, + IDTCM_FW_REG(idtcm->fw_ver, V520, SM_RESET), + &byte, sizeof(byte)); if (!err) { for (i = 0; i < 30; i++) { @@ -1040,14 +1059,14 @@ static int idtcm_state_machine_reset(struct idtcm *idtcm) read_boot_status(idtcm, &status); if (status == 0xA0) { - dev_dbg(&idtcm->client->dev, + dev_dbg(idtcm->dev, "SM_RESET completed in %d ms", i * 100); break; } } if (!status) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Timed out waiting for CM_RESET to complete"); } @@ -1144,12 +1163,12 @@ static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val) static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll) { if (index >= MAX_TOD) { - dev_err(&idtcm->client->dev, "ToD%d not supported", index); + dev_err(idtcm->dev, "ToD%d not supported", index); return -EINVAL; } if (pll >= MAX_PLL) { - dev_err(&idtcm->client->dev, "Pll%d not supported", pll); + dev_err(idtcm->dev, "Pll%d not supported", pll); return -EINVAL; } @@ -1167,7 +1186,7 @@ static int check_and_set_masks(struct idtcm *idtcm, switch (regaddr) { case TOD_MASK_ADDR: if ((val & 0xf0) || !(val & 0x0f)) { - dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val); + dev_err(idtcm->dev, "Invalid TOD mask 0x%02x", val); err = -EINVAL; } else { idtcm->tod_mask = val; @@ -1198,13 +1217,13 @@ static void display_pll_and_masks(struct idtcm *idtcm) u8 i; u8 mask; - dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask); + dev_dbg(idtcm->dev, "tod_mask = 0x%02x", idtcm->tod_mask); for (i = 0; i < MAX_TOD; i++) { mask = 1 << i; if (mask & idtcm->tod_mask) - dev_dbg(&idtcm->client->dev, + dev_dbg(idtcm->dev, "TOD%d pll = %d output_mask = 0x%04x", i, idtcm->channel[i].pll, idtcm->channel[i].output_mask); @@ -1214,6 +1233,7 @@ static void display_pll_and_masks(struct idtcm *idtcm) static int idtcm_load_firmware(struct idtcm *idtcm, struct device *dev) { + u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH); char fname[128] = FW_FILENAME; const struct firmware *fw; struct idtcm_fwrc *rec; @@ -1226,25 +1246,25 @@ static int idtcm_load_firmware(struct idtcm *idtcm, if (firmware) /* module parameter */ snprintf(fname, sizeof(fname), "%s", firmware); - dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname); + dev_info(idtcm->dev, "requesting firmware '%s'", fname); err = request_firmware(&fw, fname, dev); if (err) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); return err; } - dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size); + dev_dbg(idtcm->dev, "firmware size %zu bytes", fw->size); rec = (struct idtcm_fwrc *) fw->data; - if (contains_full_configuration(fw)) + if (contains_full_configuration(idtcm, fw)) idtcm_state_machine_reset(idtcm); for (len = fw->size; len > 0; len -= sizeof(*rec)) { if (rec->reserved) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "bad firmware, reserved field non-zero"); err = -EINVAL; } else { @@ -1263,7 +1283,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm, err = 0; /* Top (status registers) and bottom are read-only */ - if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH) + if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch) continue; /* Page size 128, last 4 bytes of page skipped */ @@ -1292,10 +1312,10 @@ static int idtcm_output_enable(struct idtcm_channel *channel, int err; u8 val; - base = get_output_base_addr(outn); + base = get_output_base_addr(idtcm->fw_ver, outn); if (!(base > 0)) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "%s - Unsupported out%d", __func__, outn); return base; } @@ -1337,8 +1357,8 @@ static int idtcm_output_mask_enable(struct idtcm_channel *channel, } static int idtcm_perout_enable(struct idtcm_channel *channel, - bool enable, - struct ptp_perout_request *perout) + struct ptp_perout_request *perout, + bool enable) { struct idtcm *idtcm = channel->idtcm; unsigned int flags = perout->flags; @@ -1351,7 +1371,7 @@ static int idtcm_perout_enable(struct idtcm_channel *channel, err = idtcm_output_enable(channel, enable, perout->index); if (err) { - dev_err(&idtcm->client->dev, "Unable to set output enable"); + dev_err(idtcm->dev, "Unable to set output enable"); return err; } @@ -1360,48 +1380,323 @@ static int idtcm_perout_enable(struct idtcm_channel *channel, } static int idtcm_get_pll_mode(struct idtcm_channel *channel, - enum pll_mode *pll_mode) + enum pll_mode *mode) { struct idtcm *idtcm = channel->idtcm; int err; u8 dpll_mode; - err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_read(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); if (err) return err; - *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; + *mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; return 0; } static int idtcm_set_pll_mode(struct idtcm_channel *channel, - enum pll_mode pll_mode) + enum pll_mode mode) { struct idtcm *idtcm = channel->idtcm; int err; u8 dpll_mode; - err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_read(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); if (err) return err; dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); - dpll_mode |= (pll_mode << PLL_MODE_SHIFT); - - channel->pll_mode = pll_mode; + dpll_mode |= (mode << PLL_MODE_SHIFT); - err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE, + err = idtcm_write(idtcm, channel->dpll_n, + IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE), &dpll_mode, sizeof(dpll_mode)); + 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) + dev_err(idtcm->dev, "Failed to set pll mode to write frequency"); + 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) + dev_err(idtcm->dev, "Failed to set pll mode to write phase"); + 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) + dev_err(idtcm->dev, "Failed to set manual reference to write frequency"); + 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) + dev_err(idtcm->dev, "Failed to set manual reference to write phase"); + 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); + if (err) + return err; + + channel->phase_pull_in = false; + + return 0; +} + +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; + + mutex_lock(idtcm->lock); + + (void)idtcm_stop_phase_pull_in(channel); + + mutex_unlock(idtcm->lock); + + /* 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 */ + + 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); + + 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: + dev_warn(idtcm->dev, + "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: + dev_err(idtcm->dev, + "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) { + dev_err(idtcm->dev, "Unable to read pll mode!"); + return err; + } + + if (mode == PLL_MODE_PLL) { + err = idtcm_get_manual_reference(channel, &ref); + if (err) { + dev_err(idtcm->dev, "Unable to read manual reference!"); + 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; +} + /* PTP Hardware Clock interface */ /* @@ -1420,8 +1715,8 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) s32 phase_50ps; s64 offset_ps; - if (channel->pll_mode != PLL_MODE_WRITE_PHASE) { - err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); + if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) { + err = channel->configure_write_phase(channel); if (err) return err; } @@ -1459,8 +1754,8 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm) u8 buf[6] = {0}; s64 fcw; - if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) { - err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); + if (channel->mode != PTP_PLL_MODE_WRITE_FREQUENCY) { + err = channel->configure_write_frequency(channel); if (err) return err; } @@ -1501,15 +1796,14 @@ static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); + mutex_lock(idtcm->lock); + err = _idtcm_gettime_immediate(channel, ts); + mutex_unlock(idtcm->lock); - err = _idtcm_gettime(channel, ts); if (err) - dev_err(&idtcm->client->dev, "Failed at line %d in %s!", + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1520,15 +1814,14 @@ static int idtcm_settime_deprecated(struct ptp_clock_info *ptp, struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); - + mutex_lock(idtcm->lock); err = _idtcm_settime_deprecated(channel, ts); + mutex_unlock(idtcm->lock); + if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1539,15 +1832,14 @@ static int idtcm_settime(struct ptp_clock_info *ptp, struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); - + mutex_lock(idtcm->lock); err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); + mutex_unlock(idtcm->lock); + if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1557,15 +1849,14 @@ static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta) struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); - + mutex_lock(idtcm->lock); err = _idtcm_adjtime_deprecated(channel, delta); + mutex_unlock(idtcm->lock); + if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1577,31 +1868,30 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta) enum scsr_tod_write_type_sel type; int err; - if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { - err = idtcm_do_phase_pull_in(channel, delta, 0); - if (err) - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - return err; - } + if (channel->phase_pull_in == true) + return 0; + + mutex_lock(idtcm->lock); - if (delta >= 0) { - ts = ns_to_timespec64(delta); - type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; + if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { + err = channel->do_phase_pull_in(channel, delta, 0); } else { - ts = ns_to_timespec64(-delta); - type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; + 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); } - mutex_lock(&idtcm->reg_lock); + mutex_unlock(idtcm->lock); - err = _idtcm_settime(channel, &ts, type); if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1611,15 +1901,14 @@ static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta) struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); - + mutex_lock(idtcm->lock); err = _idtcm_adjphase(channel, delta); + mutex_unlock(idtcm->lock); + if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - mutex_unlock(&idtcm->reg_lock); - return err; } @@ -1629,14 +1918,21 @@ static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) struct idtcm *idtcm = channel->idtcm; int err; - mutex_lock(&idtcm->reg_lock); + if (channel->phase_pull_in == true) + return 0; + if (scaled_ppm == channel->current_freq_scaled_ppm) + return 0; + + mutex_lock(idtcm->lock); err = _idtcm_adjfine(channel, scaled_ppm); + mutex_unlock(idtcm->lock); + if (err) - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); - - mutex_unlock(&idtcm->reg_lock); + else + channel->current_freq_scaled_ppm = scaled_ppm; return err; } @@ -1644,249 +1940,36 @@ static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) static int idtcm_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { - int err; struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); + struct idtcm *idtcm = channel->idtcm; + int err = -EOPNOTSUPP; + + mutex_lock(idtcm->lock); switch (rq->type) { case PTP_CLK_REQ_PEROUT: - if (!on) { - err = idtcm_perout_enable(channel, false, &rq->perout); - if (err) - dev_err(&channel->idtcm->client->dev, - "Failed at line %d in %s!", - __LINE__, __func__); - return err; - } - + if (!on) + err = idtcm_perout_enable(channel, &rq->perout, false); /* Only accept a 1-PPS aligned to the second. */ - if (rq->perout.start.nsec || rq->perout.period.sec != 1 || - rq->perout.period.nsec) - return -ERANGE; - - err = idtcm_perout_enable(channel, true, &rq->perout); - if (err) - dev_err(&channel->idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - return err; - default: - break; - } - - return -EOPNOTSUPP; -} - -static int _enable_pll_tod_sync(struct idtcm *idtcm, - u8 pll, - u8 sync_src, - u8 qn, - u8 qn_plus_1) -{ - int err; - u8 val; - u16 dpll; - u16 out0 = 0, out1 = 0; - - if (qn == 0 && qn_plus_1 == 0) - return 0; - - switch (pll) { - case 0: - dpll = DPLL_0; - if (qn) - out0 = OUTPUT_0; - if (qn_plus_1) - out1 = OUTPUT_1; + 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 1: - dpll = DPLL_1; - if (qn) - out0 = OUTPUT_2; - if (qn_plus_1) - out1 = OUTPUT_3; - break; - case 2: - dpll = DPLL_2; - if (qn) - out0 = OUTPUT_4; - if (qn_plus_1) - out1 = OUTPUT_5; - break; - case 3: - dpll = DPLL_3; - if (qn) - out0 = OUTPUT_6; - if (qn_plus_1) - out1 = OUTPUT_7; - break; - case 4: - dpll = DPLL_4; - if (qn) - out0 = OUTPUT_8; - break; - case 5: - dpll = DPLL_5; - if (qn) - out0 = OUTPUT_9; - if (qn_plus_1) - out1 = OUTPUT_8; - break; - case 6: - dpll = DPLL_6; - if (qn) - out0 = OUTPUT_10; - if (qn_plus_1) - out1 = OUTPUT_11; - break; - case 7: - dpll = DPLL_7; - if (qn) - out0 = OUTPUT_11; + case PTP_CLK_REQ_EXTTS: + err = idtcm_enable_extts(channel, rq->extts.index, + rq->extts.rsv[0], on); break; default: - return -EINVAL; - } - - /* - * Enable OUTPUT OUT_SYNC. - */ - if (out0) { - err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - - val &= ~OUT_SYNC_DISABLE; - - err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - } - - if (out1) { - err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - - val &= ~OUT_SYNC_DISABLE; - - err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); - if (err) - return err; - } - - /* enable dpll sync tod pps, must be set before dpll_mode */ - err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); - if (err) - return err; - - val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT); - val |= (sync_src << TOD_SYNC_SOURCE_SHIFT); - val |= TOD_SYNC_EN; - - return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); -} - -static int idtcm_enable_tod_sync(struct idtcm_channel *channel) -{ - struct idtcm *idtcm = channel->idtcm; - u8 pll; - u8 sync_src; - u8 qn; - u8 qn_plus_1; - u8 cfg; - int err = 0; - u16 output_mask = channel->output_mask; - u8 out8_mux = 0; - u8 out11_mux = 0; - u8 temp; - - /* - * set tod_out_sync_enable to 0. - */ - err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); - if (err) - return err; - - cfg &= ~TOD_OUT_SYNC_ENABLE; - - err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); - if (err) - return err; - - switch (channel->tod_n) { - case TOD_0: - sync_src = 0; break; - case TOD_1: - sync_src = 1; - break; - case TOD_2: - sync_src = 2; - break; - case TOD_3: - sync_src = 3; - break; - default: - return -EINVAL; } - 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; + mutex_unlock(idtcm->lock); - 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; - - for (pll = 0; pll < 8; pll++) { - qn = 0; - qn_plus_1 = 0; - - if (pll < 4) { - /* First 4 pll has 2 outputs */ - qn = output_mask & 0x1; - output_mask = output_mask >> 1; - qn_plus_1 = output_mask & 0x1; - output_mask = output_mask >> 1; - } 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; - } - } - - if (qn != 0 || qn_plus_1 != 0) - err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn, - qn_plus_1); - if (err) - return err; - } + dev_err(channel->idtcm->dev, + "Failed in %s with err %d!", __func__, err); return err; } @@ -1895,23 +1978,31 @@ static int idtcm_enable_tod(struct idtcm_channel *channel) { struct idtcm *idtcm = channel->idtcm; struct timespec64 ts = {0, 0}; + u16 tod_cfg = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_CFG); u8 cfg; int err; + /* STEELAI-366 - Temporary workaround for ts2phc compatibility */ + if (0) { + err = idtcm_output_mask_enable(channel, false); + if (err) + return err; + } + /* * Start the TOD clock ticking. */ - err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); + err = idtcm_read(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg)); if (err) return err; cfg |= TOD_ENABLE; - err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); + err = idtcm_write(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg)); if (err) return err; - if (idtcm->deprecated) + if (idtcm->fw_ver < V487) return _idtcm_settime_deprecated(channel, &ts); else return _idtcm_settime(channel, &ts, @@ -1939,12 +2030,9 @@ static void idtcm_set_version_info(struct idtcm *idtcm) snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u", major, minor, hotfix); - if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) - idtcm->deprecated = 0; - else - idtcm->deprecated = 1; + idtcm->fw_ver = idtcm_fw_version(idtcm->version); - dev_info(&idtcm->client->dev, + dev_info(idtcm->dev, "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d", major, minor, hotfix, product_id, hw_rev_id, config_select); @@ -1954,28 +2042,33 @@ static const struct ptp_clock_info idtcm_caps = { .owner = THIS_MODULE, .max_adj = 244000, .n_per_out = 12, + .n_ext_ts = MAX_TOD, .adjphase = &idtcm_adjphase, .adjfine = &idtcm_adjfine, .adjtime = &idtcm_adjtime, .gettime64 = &idtcm_gettime, .settime64 = &idtcm_settime, .enable = &idtcm_enable, + .do_aux_work = &idtcm_work_handler, }; static const struct ptp_clock_info idtcm_caps_deprecated = { .owner = THIS_MODULE, .max_adj = 244000, .n_per_out = 12, + .n_ext_ts = MAX_TOD, .adjphase = &idtcm_adjphase, .adjfine = &idtcm_adjfine, .adjtime = &idtcm_adjtime_deprecated, .gettime64 = &idtcm_gettime, .settime64 = &idtcm_settime_deprecated, .enable = &idtcm_enable, + .do_aux_work = &idtcm_work_handler, }; static int configure_channel_pll(struct idtcm_channel *channel) { + struct idtcm *idtcm = channel->idtcm; int err = 0; switch (channel->pll) { @@ -1997,7 +2090,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 2: channel->dpll_freq = DPLL_FREQ_2; - channel->dpll_n = DPLL_2; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_2); channel->hw_dpll_n = HW_DPLL_2; channel->dpll_phase = DPLL_PHASE_2; channel->dpll_ctrl_n = DPLL_CTRL_2; @@ -2013,7 +2106,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 4: channel->dpll_freq = DPLL_FREQ_4; - channel->dpll_n = DPLL_4; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_4); channel->hw_dpll_n = HW_DPLL_4; channel->dpll_phase = DPLL_PHASE_4; channel->dpll_ctrl_n = DPLL_CTRL_4; @@ -2029,7 +2122,7 @@ static int configure_channel_pll(struct idtcm_channel *channel) break; case 6: channel->dpll_freq = DPLL_FREQ_6; - channel->dpll_n = DPLL_6; + channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_6); channel->hw_dpll_n = HW_DPLL_6; channel->dpll_phase = DPLL_PHASE_6; channel->dpll_ctrl_n = DPLL_CTRL_6; @@ -2050,50 +2143,104 @@ static int configure_channel_pll(struct idtcm_channel *channel) return err; } -static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) +/* + * 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) { - struct idtcm_channel *channel; + struct idtcm *idtcm = channel->idtcm; + u8 mbuf[8] = {0}; + u8 nbuf[2] = {0}; + u32 fodFreq; int err; + u64 m; + u16 n; - if (!(index < MAX_TOD)) - return -EINVAL; - - channel = &idtcm->channel[index]; + err = idtcm_read(idtcm, channel->dpll_ctrl_n, + DPLL_CTRL_DPLL_FOD_FREQ, mbuf, 6); + if (err) + return 0; - /* Set pll addresses */ - err = configure_channel_pll(channel); + err = idtcm_read(idtcm, channel->dpll_ctrl_n, + DPLL_CTRL_DPLL_FOD_FREQ + 6, nbuf, 2); if (err) - return err; + return 0; + + m = get_unaligned_le64(mbuf); + n = get_unaligned_le16(nbuf); + + if (n == 0) + n = 1; + + fodFreq = (u32)div_u64(m, n); + if (fodFreq >= 500000000) + return 18 * (u32)div_u64(NSEC_PER_SEC, fodFreq); + + return 0; +} + +static int configure_channel_tod(struct idtcm_channel *channel, u32 index) +{ + enum fw_version fw_ver = channel->idtcm->fw_ver; /* Set tod addresses */ switch (index) { case 0: - channel->tod_read_primary = TOD_READ_PRIMARY_0; - channel->tod_write = TOD_WRITE_0; - channel->tod_n = TOD_0; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0); + 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; break; case 1: - channel->tod_read_primary = TOD_READ_PRIMARY_1; - channel->tod_write = TOD_WRITE_1; - channel->tod_n = TOD_1; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1); + 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; break; case 2: - channel->tod_read_primary = TOD_READ_PRIMARY_2; - channel->tod_write = TOD_WRITE_2; - channel->tod_n = TOD_2; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2); + 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; break; case 3: - channel->tod_read_primary = TOD_READ_PRIMARY_3; - channel->tod_write = TOD_WRITE_3; - channel->tod_n = TOD_3; + channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3); + 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; break; default: return -EINVAL; } + return 0; +} + +static int idtcm_enable_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; + channel->current_freq_scaled_ppm = 0; + + /* Set pll addresses */ + err = configure_channel_pll(channel); + if (err) + return err; - if (idtcm->deprecated) + /* Set tod addresses */ + err = configure_channel_tod(channel, index); + if (err) + return err; + + if (idtcm->fw_ver < V487) channel->caps = idtcm_caps_deprecated; else channel->caps = idtcm_caps; @@ -2101,30 +2248,19 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) snprintf(channel->caps.name, sizeof(channel->caps.name), "IDT CM TOD%u", index); - if (!idtcm->deprecated) { - err = idtcm_enable_tod_sync(channel); - if (err) { - dev_err(&idtcm->client->dev, - "Failed at line %d in %s!", __LINE__, __func__); - return err; - } - } - - /* Sync pll mode with hardware */ - err = idtcm_get_pll_mode(channel, &channel->pll_mode); - if (err) { - dev_err(&idtcm->client->dev, - "Error: %s - Unable to read pll mode", __func__); + err = initialize_dco_operating_mode(channel); + if (err) return err; - } err = idtcm_enable_tod(channel); if (err) { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "Failed at line %d in %s!", __LINE__, __func__); return err; } + channel->dco_delay = idtcm_get_dco_delay(channel); + channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); if (IS_ERR(channel->ptp_clock)) { @@ -2136,12 +2272,59 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) if (!channel->ptp_clock) return -ENOTSUPP; - dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d", + dev_info(idtcm->dev, "PLL%d registered as ptp%d", index, channel->ptp_clock->index); return 0; } +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); + int err, i; + + if (idtcm->extts_mask == 0) + return; + + mutex_lock(idtcm->lock); + for (i = 0; i < MAX_TOD; i++) { + u8 mask = 1 << i; + + if (idtcm->extts_mask & mask) { + err = idtcm_extts_check_channel(idtcm, i); + /* trigger clears itself, so clear the mask */ + if (err == 0) + idtcm->extts_mask &= ~mask; + } + } + + if (idtcm->extts_mask) + schedule_delayed_work(&idtcm->extts_work, + msecs_to_jiffies(EXTTS_PERIOD_MS)); + mutex_unlock(idtcm->lock); +} + static void ptp_clock_unregister_all(struct idtcm *idtcm) { u8 i; @@ -2149,7 +2332,6 @@ static void ptp_clock_unregister_all(struct idtcm *idtcm) for (i = 0; i < MAX_TOD; i++) { channel = &idtcm->channel[i]; - if (channel->ptp_clock) ptp_clock_unregister(channel->ptp_clock); } @@ -2158,6 +2340,7 @@ static void ptp_clock_unregister_all(struct idtcm *idtcm) static void set_default_masks(struct idtcm *idtcm) { idtcm->tod_mask = DEFAULT_TOD_MASK; + idtcm->extts_mask = 0; idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL; idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL; @@ -2170,158 +2353,86 @@ static void set_default_masks(struct idtcm *idtcm) idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3; } -static int idtcm_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int idtcm_probe(struct platform_device *pdev) { + struct rsmu_ddata *ddata = dev_get_drvdata(pdev->dev.parent); struct idtcm *idtcm; int err; u8 i; - /* Unused for now */ - (void)id; - - idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL); + idtcm = devm_kzalloc(&pdev->dev, sizeof(struct idtcm), GFP_KERNEL); if (!idtcm) return -ENOMEM; - idtcm->client = client; - idtcm->page_offset = 0xff; + idtcm->dev = &pdev->dev; + idtcm->mfd = pdev->dev.parent; + idtcm->lock = &ddata->lock; + idtcm->regmap = ddata->regmap; idtcm->calculate_overhead_flag = 0; + INIT_DELAYED_WORK(&idtcm->extts_work, idtcm_extts_check); + set_default_masks(idtcm); - mutex_init(&idtcm->reg_lock); - mutex_lock(&idtcm->reg_lock); + mutex_lock(idtcm->lock); idtcm_set_version_info(idtcm); - err = idtcm_load_firmware(idtcm, &client->dev); + err = idtcm_load_firmware(idtcm, &pdev->dev); + if (err) - dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err); + dev_warn(idtcm->dev, "loading firmware failed with %d", err); wait_for_chip_ready(idtcm); if (idtcm->tod_mask) { for (i = 0; i < MAX_TOD; i++) { - if (idtcm->tod_mask & (1 << i)) { + if (idtcm->tod_mask & (1 << i)) err = idtcm_enable_channel(idtcm, i); - if (err) { - dev_err(&idtcm->client->dev, - "idtcm_enable_channel %d failed!", i); - break; - } + else + err = idtcm_enable_extts_channel(idtcm, i); + if (err) { + dev_err(idtcm->dev, + "idtcm_enable_channel %d failed!", i); + break; } } } else { - dev_err(&idtcm->client->dev, + dev_err(idtcm->dev, "no PLLs flagged as PHCs, nothing to do"); err = -ENODEV; } - mutex_unlock(&idtcm->reg_lock); + mutex_unlock(idtcm->lock); if (err) { ptp_clock_unregister_all(idtcm); return err; } - i2c_set_clientdata(client, idtcm); + platform_set_drvdata(pdev, idtcm); return 0; } -static int idtcm_remove(struct i2c_client *client) +static int idtcm_remove(struct platform_device *pdev) { - struct idtcm *idtcm = i2c_get_clientdata(client); + struct idtcm *idtcm = platform_get_drvdata(pdev); ptp_clock_unregister_all(idtcm); - mutex_destroy(&idtcm->reg_lock); + cancel_delayed_work_sync(&idtcm->extts_work); return 0; } -#ifdef CONFIG_OF -static const struct of_device_id idtcm_dt_id[] = { - { .compatible = "idt,8a34000" }, - { .compatible = "idt,8a34001" }, - { .compatible = "idt,8a34002" }, - { .compatible = "idt,8a34003" }, - { .compatible = "idt,8a34004" }, - { .compatible = "idt,8a34005" }, - { .compatible = "idt,8a34006" }, - { .compatible = "idt,8a34007" }, - { .compatible = "idt,8a34008" }, - { .compatible = "idt,8a34009" }, - { .compatible = "idt,8a34010" }, - { .compatible = "idt,8a34011" }, - { .compatible = "idt,8a34012" }, - { .compatible = "idt,8a34013" }, - { .compatible = "idt,8a34014" }, - { .compatible = "idt,8a34015" }, - { .compatible = "idt,8a34016" }, - { .compatible = "idt,8a34017" }, - { .compatible = "idt,8a34018" }, - { .compatible = "idt,8a34019" }, - { .compatible = "idt,8a34040" }, - { .compatible = "idt,8a34041" }, - { .compatible = "idt,8a34042" }, - { .compatible = "idt,8a34043" }, - { .compatible = "idt,8a34044" }, - { .compatible = "idt,8a34045" }, - { .compatible = "idt,8a34046" }, - { .compatible = "idt,8a34047" }, - { .compatible = "idt,8a34048" }, - { .compatible = "idt,8a34049" }, - {}, -}; -MODULE_DEVICE_TABLE(of, idtcm_dt_id); -#endif - -static const struct i2c_device_id idtcm_i2c_id[] = { - { "8a34000" }, - { "8a34001" }, - { "8a34002" }, - { "8a34003" }, - { "8a34004" }, - { "8a34005" }, - { "8a34006" }, - { "8a34007" }, - { "8a34008" }, - { "8a34009" }, - { "8a34010" }, - { "8a34011" }, - { "8a34012" }, - { "8a34013" }, - { "8a34014" }, - { "8a34015" }, - { "8a34016" }, - { "8a34017" }, - { "8a34018" }, - { "8a34019" }, - { "8a34040" }, - { "8a34041" }, - { "8a34042" }, - { "8a34043" }, - { "8a34044" }, - { "8a34045" }, - { "8a34046" }, - { "8a34047" }, - { "8a34048" }, - { "8a34049" }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id); - -static struct i2c_driver idtcm_driver = { +static struct platform_driver idtcm_driver = { .driver = { - .of_match_table = of_match_ptr(idtcm_dt_id), - .name = "idtcm", + .name = "8a3400x-phc", }, - .probe = idtcm_probe, - .remove = idtcm_remove, - .id_table = idtcm_i2c_id, + .probe = idtcm_probe, + .remove = idtcm_remove, }; -module_i2c_driver(idtcm_driver); +module_platform_driver(idtcm_driver); diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h index fb323271063e..0f3059ae1fff 100644 --- a/drivers/ptp/ptp_clockmatrix.h +++ b/drivers/ptp/ptp_clockmatrix.h @@ -9,8 +9,8 @@ #define PTP_IDTCLOCKMATRIX_H #include <linux/ktime.h> - -#include "idt8a340_reg.h" +#include <linux/mfd/idt8a340_reg.h> +#include <linux/regmap.h> #define FW_FILENAME "idtcm.bin" #define MAX_TOD (4) @@ -44,7 +44,6 @@ #define DEFAULT_TOD2_PTP_PLL (2) #define DEFAULT_TOD3_PTP_PLL (3) -#define POST_SM_RESET_DELAY_MS (3000) #define PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED (150000) #define PHASE_PULL_IN_THRESHOLD_NS (15000) #define TOD_WRITE_OVERHEAD_COUNT_MAX (2) @@ -57,66 +56,26 @@ #define IDTCM_MAX_WRITE_COUNT (512) -#define FULL_FW_CFG_BYTES (SCRATCH - GPIO_USER_CONTROL) -#define FULL_FW_CFG_SKIPPED_BYTES (((SCRATCH >> 7) \ - - (GPIO_USER_CONTROL >> 7)) \ - * 4) /* 4 bytes skipped every 0x80 */ - -/* Values of DPLL_N.DPLL_MODE.PLL_MODE */ -enum pll_mode { - PLL_MODE_MIN = 0, - PLL_MODE_NORMAL = PLL_MODE_MIN, - PLL_MODE_WRITE_PHASE = 1, - PLL_MODE_WRITE_FREQUENCY = 2, - PLL_MODE_GPIO_INC_DEC = 3, - PLL_MODE_SYNTHESIS = 4, - PLL_MODE_PHASE_MEASUREMENT = 5, - PLL_MODE_DISABLED = 6, - PLL_MODE_MAX = PLL_MODE_DISABLED, -}; - -enum hw_tod_write_trig_sel { - HW_TOD_WR_TRIG_SEL_MIN = 0, - HW_TOD_WR_TRIG_SEL_MSB = HW_TOD_WR_TRIG_SEL_MIN, - HW_TOD_WR_TRIG_SEL_RESERVED = 1, - HW_TOD_WR_TRIG_SEL_TOD_PPS = 2, - HW_TOD_WR_TRIG_SEL_IRIGB_PPS = 3, - HW_TOD_WR_TRIG_SEL_PWM_PPS = 4, - HW_TOD_WR_TRIG_SEL_GPIO = 5, - HW_TOD_WR_TRIG_SEL_FOD_SYNC = 6, - WR_TRIG_SEL_MAX = HW_TOD_WR_TRIG_SEL_FOD_SYNC, -}; - -/* 4.8.7 only */ -enum scsr_tod_write_trig_sel { - SCSR_TOD_WR_TRIG_SEL_DISABLE = 0, - SCSR_TOD_WR_TRIG_SEL_IMMEDIATE = 1, - SCSR_TOD_WR_TRIG_SEL_REFCLK = 2, - SCSR_TOD_WR_TRIG_SEL_PWMPPS = 3, - SCSR_TOD_WR_TRIG_SEL_TODPPS = 4, - SCSR_TOD_WR_TRIG_SEL_SYNCFOD = 5, - SCSR_TOD_WR_TRIG_SEL_GPIO = 6, - SCSR_TOD_WR_TRIG_SEL_MAX = SCSR_TOD_WR_TRIG_SEL_GPIO, -}; +#define PHASE_PULL_IN_MAX_PPB (144000) +#define PHASE_PULL_IN_MIN_THRESHOLD_NS (2) -/* 4.8.7 only */ -enum scsr_tod_write_type_sel { - SCSR_TOD_WR_TYPE_SEL_ABSOLUTE = 0, - SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS = 1, - SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS = 2, - SCSR_TOD_WR_TYPE_SEL_MAX = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS, +/* + * Return register address based on passed in firmware version + */ +#define IDTCM_FW_REG(FW, VER, REG) (((FW) < (VER)) ? (REG) : (REG##_##VER)) +enum fw_version { + V_DEFAULT = 0, + V487 = 1, + V520 = 2, }; -/* Values STATUS.DPLL_SYS_STATUS.DPLL_SYS_STATE */ -enum dpll_state { - DPLL_STATE_MIN = 0, - DPLL_STATE_FREERUN = DPLL_STATE_MIN, - DPLL_STATE_LOCKACQ = 1, - DPLL_STATE_LOCKREC = 2, - DPLL_STATE_LOCKED = 3, - DPLL_STATE_HOLDOVER = 4, - DPLL_STATE_OPEN_LOOP = 5, - DPLL_STATE_MAX = DPLL_STATE_OPEN_LOOP, +/* PTP PLL Mode */ +enum ptp_pll_mode { + PTP_PLL_MODE_MIN = 0, + PTP_PLL_MODE_WRITE_FREQUENCY = PTP_PLL_MODE_MIN, + PTP_PLL_MODE_WRITE_PHASE, + PTP_PLL_MODE_UNSUPPORTED, + PTP_PLL_MODE_MAX = PTP_PLL_MODE_UNSUPPORTED, }; struct idtcm; @@ -134,26 +93,40 @@ struct idtcm_channel { u16 tod_write; u16 tod_n; u16 hw_dpll_n; - enum pll_mode pll_mode; + u8 sync_src; + enum ptp_pll_mode mode; + int (*configure_write_frequency)(struct idtcm_channel *channel); + int (*configure_write_phase)(struct idtcm_channel *channel); + int (*do_phase_pull_in)(struct idtcm_channel *channel, + s32 offset_ns, u32 max_ffo_ppb); + s32 current_freq_scaled_ppm; + bool phase_pull_in; + u32 dco_delay; + /* last input trigger for extts */ + u8 refn; u8 pll; u16 output_mask; }; struct idtcm { struct idtcm_channel channel[MAX_TOD]; - struct i2c_client *client; - u8 page_offset; + struct device *dev; u8 tod_mask; char version[16]; - u8 deprecated; - + enum fw_version fw_ver; + /* Polls for external time stamps */ + u8 extts_mask; + struct delayed_work extts_work; + /* Remember the ptp channel to report extts */ + struct idtcm_channel *event_channel[MAX_TOD]; + /* Mutex to protect operations from being interrupted */ + struct mutex *lock; + struct device *mfd; + struct regmap *regmap; /* Overhead calculation for adjtime */ u8 calculate_overhead_flag; s64 tod_write_overhead_ns; ktime_t start_time; - - /* Protects I2C read/modify/write registers from concurrent access */ - struct mutex reg_lock; }; struct idtcm_fwrc { diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index caf9b37c5eb1..0f1b5a7d2a89 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -4,6 +4,7 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/debugfs.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/serial_8250.h> @@ -72,7 +73,7 @@ struct tod_reg { u32 status; u32 uart_polarity; u32 version; - u32 correction_sec; + u32 adj_sec; u32 __pad0[3]; u32 uart_baud; u32 __pad1[3]; @@ -124,6 +125,55 @@ struct img_reg { u32 version; }; +struct gpio_reg { + u32 gpio1; + u32 __pad0; + u32 gpio2; + u32 __pad1; +}; + +struct irig_master_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; + u32 mode_ctrl; +}; + +#define IRIG_M_CTRL_ENABLE BIT(0) + +struct irig_slave_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; + u32 mode_ctrl; +}; + +#define IRIG_S_CTRL_ENABLE BIT(0) + +struct dcf_master_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; +}; + +#define DCF_M_CTRL_ENABLE BIT(0) + +struct dcf_slave_reg { + u32 ctrl; + u32 status; + u32 __pad0; + u32 version; + u32 adj_sec; +}; + +#define DCF_S_CTRL_ENABLE BIT(0) + struct ptp_ocp_flash_info { const char *name; int pci_offset; @@ -131,11 +181,17 @@ struct ptp_ocp_flash_info { void *data; }; -struct ptp_ocp_ext_info { +struct ptp_ocp_i2c_info { const char *name; + unsigned long fixed_rate; + size_t data_size; + void *data; +}; + +struct ptp_ocp_ext_info { int index; irqreturn_t (*irq_fcn)(int irq, void *priv); - int (*enable)(void *priv, bool enable); + int (*enable)(void *priv, u32 req, bool enable); }; struct ptp_ocp_ext_src { @@ -153,9 +209,17 @@ struct ptp_ocp { struct tod_reg __iomem *tod; struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; + struct gpio_reg __iomem *pps_select; + struct gpio_reg __iomem *sma; + struct irig_master_reg __iomem *irig_out; + struct irig_slave_reg __iomem *irig_in; + struct dcf_master_reg __iomem *dcf_out; + struct dcf_slave_reg __iomem *dcf_in; + struct tod_reg __iomem *nmea_out; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; + struct ptp_ocp_ext_src *ts2; struct img_reg __iomem *image; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; @@ -163,16 +227,25 @@ struct ptp_ocp { struct platform_device *spi_flash; struct clk_hw *i2c_clk; struct timer_list watchdog; + struct dentry *debug_root; time64_t gnss_lost; int id; int n_irqs; int gnss_port; + int gnss2_port; int mac_port; /* miniature atomic clock */ + int nmea_port; u8 serial[6]; - int flash_start; bool has_serial; + u32 pps_req_map; + int flash_start; + u32 utc_tai_offset; + u32 ts_window_adjust; }; +#define OCP_REQ_TIMESTAMP BIT(0) +#define OCP_REQ_PPS BIT(1) + struct ocp_resource { unsigned long offset; int size; @@ -180,6 +253,7 @@ struct ocp_resource { int (*setup)(struct ptp_ocp *bp, struct ocp_resource *r); void *extra; unsigned long bp_offset; + const char * const name; }; static int ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r); @@ -189,7 +263,7 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); -static int ptp_ocp_ts_enable(void *priv, bool enable); +static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ @@ -197,7 +271,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); }) #define OCP_RES_LOCATION(member) \ - .bp_offset = offsetof(struct ptp_ocp, member) + .name = #member, .bp_offset = offsetof(struct ptp_ocp, member) #define OCP_MEM_RESOURCE(member) \ OCP_RES_LOCATION(member), .setup = ptp_ocp_register_mem @@ -215,16 +289,17 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext /* This is the MSI vector mapping used. - * 0: N/C + * 0: TS3 (and PPS) * 1: TS0 * 2: TS1 - * 3: GPS - * 4: GPS2 (n/c) + * 3: GNSS + * 4: GNSS2 * 5: MAC - * 6: SPI IMU (inertial measurement unit) - * 7: I2C oscillator - * 8: HWICAP + * 6: TS2 + * 7: I2C controller + * 8: HWICAP (notused) * 9: SPI Flash + * 10: NMEA */ static struct ocp_resource ocp_fb_resource[] = { @@ -236,7 +311,7 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_EXT_RESOURCE(ts0), .offset = 0x01010000, .size = 0x10000, .irq_vec = 1, .extra = &(struct ptp_ocp_ext_info) { - .name = "ts0", .index = 0, + .index = 0, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, @@ -245,7 +320,25 @@ static struct ocp_resource ocp_fb_resource[] = { OCP_EXT_RESOURCE(ts1), .offset = 0x01020000, .size = 0x10000, .irq_vec = 2, .extra = &(struct ptp_ocp_ext_info) { - .name = "ts1", .index = 1, + .index = 1, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts2), + .offset = 0x01060000, .size = 0x10000, .irq_vec = 6, + .extra = &(struct ptp_ocp_ext_info) { + .index = 2, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(pps), + .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, @@ -263,22 +356,62 @@ static struct ocp_resource ocp_fb_resource[] = { .offset = 0x01050000, .size = 0x10000, }, { + OCP_MEM_RESOURCE(irig_in), + .offset = 0x01070000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(irig_out), + .offset = 0x01080000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(dcf_in), + .offset = 0x01090000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(dcf_out), + .offset = 0x010A0000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(nmea_out), + .offset = 0x010B0000, .size = 0x10000, + }, + { OCP_MEM_RESOURCE(image), .offset = 0x00020000, .size = 0x1000, }, { + OCP_MEM_RESOURCE(pps_select), + .offset = 0x00130000, .size = 0x1000, + }, + { + OCP_MEM_RESOURCE(sma), + .offset = 0x00140000, .size = 0x1000, + }, + { OCP_I2C_RESOURCE(i2c_ctrl), .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, + .extra = &(struct ptp_ocp_i2c_info) { + .name = "xiic-i2c", + .fixed_rate = 50000000, + }, }, { OCP_SERIAL_RESOURCE(gnss_port), .offset = 0x00160000 + 0x1000, .irq_vec = 3, }, { + OCP_SERIAL_RESOURCE(gnss2_port), + .offset = 0x00170000 + 0x1000, .irq_vec = 4, + }, + { OCP_SERIAL_RESOURCE(mac_port), .offset = 0x00180000 + 0x1000, .irq_vec = 5, }, { + OCP_SERIAL_RESOURCE(nmea_port), + .offset = 0x00190000 + 0x1000, .irq_vec = 10, + }, + { OCP_SPI_RESOURCE(spi_flash), .offset = 0x00310000, .size = 0x10000, .irq_vec = 9, .extra = &(struct ptp_ocp_flash_info) { @@ -309,10 +442,12 @@ MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id); static DEFINE_MUTEX(ptp_ocp_lock); static DEFINE_IDR(ptp_ocp_idr); -static struct { +struct ocp_selector { const char *name; int value; -} ptp_ocp_clock[] = { +}; + +static struct ocp_selector ptp_ocp_clock[] = { { .name = "NONE", .value = 0 }, { .name = "TOD", .value = 1 }, { .name = "IRIG", .value = 2 }, @@ -322,33 +457,71 @@ static struct { { .name = "DCF", .value = 6 }, { .name = "REGS", .value = 0xfe }, { .name = "EXT", .value = 0xff }, + { } +}; + +static struct ocp_selector ptp_ocp_sma_in[] = { + { .name = "10Mhz", .value = 0x00 }, + { .name = "PPS1", .value = 0x01 }, + { .name = "PPS2", .value = 0x02 }, + { .name = "TS1", .value = 0x04 }, + { .name = "TS2", .value = 0x08 }, + { .name = "IRIG", .value = 0x10 }, + { .name = "DCF", .value = 0x20 }, + { } +}; + +static struct ocp_selector ptp_ocp_sma_out[] = { + { .name = "10Mhz", .value = 0x00 }, + { .name = "PHC", .value = 0x01 }, + { .name = "MAC", .value = 0x02 }, + { .name = "GNSS", .value = 0x04 }, + { .name = "GNSS2", .value = 0x08 }, + { .name = "IRIG", .value = 0x10 }, + { .name = "DCF", .value = 0x20 }, + { } }; static const char * -ptp_ocp_clock_name_from_val(int val) +ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val) { int i; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) - if (ptp_ocp_clock[i].value == val) - return ptp_ocp_clock[i].name; + for (i = 0; tbl[i].name; i++) + if (tbl[i].value == val) + return tbl[i].name; return NULL; } static int -ptp_ocp_clock_val_from_name(const char *name) +ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name) { - const char *clk; + const char *select; int i; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) { - clk = ptp_ocp_clock[i].name; - if (!strncasecmp(name, clk, strlen(clk))) - return ptp_ocp_clock[i].value; + for (i = 0; tbl[i].name; i++) { + select = tbl[i].name; + if (!strncasecmp(name, select, strlen(select))) + return tbl[i].value; } return -EINVAL; } +static ssize_t +ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf) +{ + ssize_t count; + int i; + + count = 0; + for (i = 0; tbl[i].name; i++) + count += sysfs_emit_at(buf, count, "%s ", tbl[i].name); + if (count) + count--; + count += sysfs_emit_at(buf, count, "\n"); + return count; +} + static int __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, struct ptp_system_timestamp *sts) @@ -356,10 +529,9 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, u32 ctrl, time_sec, time_ns; int i; - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_READ_TIME_REQ; - ptp_read_system_prets(sts); + + ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); for (i = 0; i < 100; i++) { @@ -369,6 +541,12 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, } ptp_read_system_postts(sts); + if (sts && bp->ts_window_adjust) { + s64 ns = timespec64_to_ns(&sts->post_ts); + + sts->post_ts = ns_to_timespec64(ns - bp->ts_window_adjust); + } + time_ns = ioread32(&bp->reg->time_ns); time_sec = ioread32(&bp->reg->time_sec); @@ -408,8 +586,7 @@ __ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts) iowrite32(time_ns, &bp->reg->adjust_ns); iowrite32(time_sec, &bp->reg->adjust_sec); - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ADJUST_TIME; + ctrl = OCP_CTRL_ADJUST_TIME | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* restore clock selection */ @@ -422,9 +599,6 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); unsigned long flags; - if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC) - return 0; - spin_lock_irqsave(&bp->lock, flags); __ptp_ocp_settime_locked(bp, ts); spin_unlock_irqrestore(&bp->lock, flags); @@ -432,26 +606,39 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) return 0; } +static void +__ptp_ocp_adjtime_locked(struct ptp_ocp *bp, u64 adj_val) +{ + u32 select, ctrl; + + select = ioread32(&bp->reg->select); + iowrite32(OCP_SELECT_CLK_REG, &bp->reg->select); + + iowrite32(adj_val, &bp->reg->offset_ns); + iowrite32(adj_val & 0x7f, &bp->reg->offset_window_ns); + + ctrl = OCP_CTRL_ADJUST_OFFSET | OCP_CTRL_ENABLE; + iowrite32(ctrl, &bp->reg->ctrl); + + /* restore clock selection */ + iowrite32(select >> 16, &bp->reg->select); +} + static int ptp_ocp_adjtime(struct ptp_clock_info *ptp_info, s64 delta_ns) { struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); - struct timespec64 ts; unsigned long flags; - int err; + u32 adj_ns, sign; - if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC) - return 0; + sign = delta_ns < 0 ? BIT(31) : 0; + adj_ns = sign ? -delta_ns : delta_ns; spin_lock_irqsave(&bp->lock, flags); - err = __ptp_ocp_gettime_locked(bp, &ts, NULL); - if (likely(!err)) { - timespec64_add_ns(&ts, delta_ns); - __ptp_ocp_settime_locked(bp, &ts); - } + __ptp_ocp_adjtime_locked(bp, sign | adj_ns); spin_unlock_irqrestore(&bp->lock, flags); - return err; + return 0; } static int @@ -464,7 +651,7 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) } static int -ptp_ocp_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) +ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) { return -EOPNOTSUPP; } @@ -475,10 +662,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, { struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); struct ptp_ocp_ext_src *ext = NULL; + u32 req; int err; switch (rq->type) { case PTP_CLK_REQ_EXTTS: + req = OCP_REQ_TIMESTAMP; switch (rq->extts.index) { case 0: ext = bp->ts0; @@ -486,18 +675,33 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, case 1: ext = bp->ts1; break; + case 2: + ext = bp->ts2; + break; + case 3: + ext = bp->pps; + break; } break; case PTP_CLK_REQ_PPS: + req = OCP_REQ_PPS; ext = bp->pps; break; + case PTP_CLK_REQ_PEROUT: + if (on && + (rq->perout.period.sec != 1 || rq->perout.period.nsec != 0)) + return -EINVAL; + /* This is a request for 1PPS on an output SMA. + * Allow, but assume manual configuration. + */ + return 0; default: return -EOPNOTSUPP; } err = -ENXIO; if (ext) - err = ext->info->enable(ext, on); + err = ext->info->enable(ext, req, on); return err; } @@ -510,10 +714,11 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .settime64 = ptp_ocp_settime, .adjtime = ptp_ocp_adjtime, .adjfine = ptp_ocp_null_adjfine, - .adjphase = ptp_ocp_adjphase, + .adjphase = ptp_ocp_null_adjphase, .enable = ptp_ocp_enable, .pps = true, - .n_ext_ts = 2, + .n_ext_ts = 4, + .n_per_out = 1, }; static void @@ -526,8 +731,7 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) iowrite32(0, &bp->reg->drift_ns); - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ADJUST_DRIFT; + ctrl = OCP_CTRL_ADJUST_DRIFT | OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* restore clock selection */ @@ -559,6 +763,28 @@ ptp_ocp_watchdog(struct timer_list *t) mod_timer(&bp->watchdog, jiffies + HZ); } +static void +ptp_ocp_estimate_pci_timing(struct ptp_ocp *bp) +{ + ktime_t start, end; + ktime_t delay; + u32 ctrl; + + ctrl = ioread32(&bp->reg->ctrl); + ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE; + + iowrite32(ctrl, &bp->reg->ctrl); + + start = ktime_get_ns(); + + ctrl = ioread32(&bp->reg->ctrl); + + end = ktime_get_ns(); + + delay = end - start; + bp->ts_window_adjust = (delay >> 5) * 3; +} + static int ptp_ocp_init_clock(struct ptp_ocp *bp) { @@ -566,9 +792,7 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) bool sync; u32 ctrl; - /* make sure clock is enabled */ - ctrl = ioread32(&bp->reg->ctrl); - ctrl |= OCP_CTRL_ENABLE; + ctrl = OCP_CTRL_ENABLE; iowrite32(ctrl, &bp->reg->ctrl); /* NO DRIFT Correction */ @@ -587,23 +811,58 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) return -ENODEV; } + ptp_ocp_estimate_pci_timing(bp); + sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; if (!sync) { - ktime_get_real_ts64(&ts); + ktime_get_clocktai_ts64(&ts); ptp_ocp_settime(&bp->ptp_info, &ts); } - if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL)) - dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n", - ts.tv_sec, ts.tv_nsec, - sync ? "in-sync" : "UNSYNCED"); - timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0); - mod_timer(&bp->watchdog, jiffies + HZ); + /* If there is a clock supervisor, then enable the watchdog */ + if (bp->pps_to_clk) { + timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0); + mod_timer(&bp->watchdog, jiffies + HZ); + } return 0; } static void +ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + bp->utc_tai_offset = val; + + if (bp->irig_out) + iowrite32(val, &bp->irig_out->adj_sec); + if (bp->dcf_out) + iowrite32(val, &bp->dcf_out->adj_sec); + if (bp->nmea_out) + iowrite32(val, &bp->nmea_out->adj_sec); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void +ptp_ocp_tod_init(struct ptp_ocp *bp) +{ + u32 ctrl, reg; + + ctrl = ioread32(&bp->tod->ctrl); + ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE; + ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B); + iowrite32(ctrl, &bp->tod->ctrl); + + reg = ioread32(&bp->tod->utc_status); + if (reg & TOD_STATUS_UTC_VALID) + ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK); +} + +static void ptp_ocp_tod_info(struct ptp_ocp *bp) { static const char * const proto_name[] = { @@ -621,11 +880,6 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) version >> 24, (version >> 16) & 0xff, version & 0xffff); ctrl = ioread32(&bp->tod->ctrl); - ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE; - ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B); - iowrite32(ctrl, &bp->tod->ctrl); - - ctrl = ioread32(&bp->tod->ctrl); idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0; idx += (ctrl >> 16) & 3; dev_info(&bp->pdev->dev, "control: %x\n", ctrl); @@ -639,7 +893,7 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) reg = ioread32(&bp->tod->status); dev_info(&bp->pdev->dev, "status: %x\n", reg); - reg = ioread32(&bp->tod->correction_sec); + reg = ioread32(&bp->tod->adj_sec); dev_info(&bp->pdev->dev, "correction: %d\n", reg); reg = ioread32(&bp->tod->utc_status); @@ -695,6 +949,9 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp) struct device *dev; int err; + if (!bp->i2c_ctrl) + return; + dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); if (!dev) { dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); @@ -720,21 +977,6 @@ out: put_device(dev); } -static void -ptp_ocp_info(struct ptp_ocp *bp) -{ - u32 version, select; - - version = ioread32(&bp->reg->version); - select = ioread32(&bp->reg->select); - dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n", - version >> 24, (version >> 16) & 0xff, version & 0xffff, - ptp_ocp_clock_name_from_val(select >> 16), - ptp_clock_index(bp->ptp)); - - ptp_ocp_tod_info(bp); -} - static struct device * ptp_ocp_find_flash(struct ptp_ocp *bp) { @@ -910,18 +1152,6 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) unsigned long start; int id; - /* XXX hack to work around old FPGA */ - if (bp->n_irqs < 10) { - dev_err(&bp->pdev->dev, "FPGA does not have SPI devices\n"); - return 0; - } - - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "spi device irq %d out of range\n", - r->irq_vec); - return 0; - } - start = pci_resource_start(pdev, 0) + r->offset; ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); @@ -944,41 +1174,41 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) static struct platform_device * ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id) { + struct ptp_ocp_i2c_info *info; struct resource res[2]; unsigned long start; + info = r->extra; start = pci_resource_start(pdev, 0) + r->offset; ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); - return platform_device_register_resndata(&pdev->dev, "xiic-i2c", - id, res, 2, NULL, 0); + return platform_device_register_resndata(&pdev->dev, info->name, + id, res, 2, + info->data, info->data_size); } static int ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) { struct pci_dev *pdev = bp->pdev; + struct ptp_ocp_i2c_info *info; struct platform_device *p; struct clk_hw *clk; char buf[32]; int id; - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "i2c device irq %d out of range\n", - r->irq_vec); - return 0; - } - + info = r->extra; id = pci_dev_id(bp->pdev); sprintf(buf, "AXI.%d", id); - clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0, 50000000); + clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0, + info->fixed_rate); if (IS_ERR(clk)) return PTR_ERR(clk); bp->i2c_clk = clk; - sprintf(buf, "xiic-i2c.%d", id); + sprintf(buf, "%s.%d", info->name, id); devm_clk_hw_register_clkdev(&pdev->dev, clk, NULL, buf); p = ptp_ocp_i2c_bus(bp->pdev, r, id); if (IS_ERR(p)) @@ -997,26 +1227,51 @@ ptp_ocp_ts_irq(int irq, void *priv) struct ptp_clock_event ev; u32 sec, nsec; + if (ext == ext->bp->pps) { + if (ext->bp->pps_req_map & OCP_REQ_PPS) { + ev.type = PTP_CLOCK_PPS; + ptp_clock_event(ext->bp->ptp, &ev); + } + + if ((ext->bp->pps_req_map & ~OCP_REQ_PPS) == 0) + goto out; + } + /* XXX should fix API - this converts s/ns -> ts -> s/ns */ sec = ioread32(®->time_sec); nsec = ioread32(®->time_ns); ev.type = PTP_CLOCK_EXTTS; ev.index = ext->info->index; - ev.timestamp = sec * 1000000000ULL + nsec; + ev.timestamp = sec * NSEC_PER_SEC + nsec; ptp_clock_event(ext->bp->ptp, &ev); +out: iowrite32(1, ®->intr); /* write 1 to ack */ return IRQ_HANDLED; } static int -ptp_ocp_ts_enable(void *priv, bool enable) +ptp_ocp_ts_enable(void *priv, u32 req, bool enable) { struct ptp_ocp_ext_src *ext = priv; struct ts_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + + if (ext == bp->pps) { + u32 old_map = bp->pps_req_map; + + if (enable) + bp->pps_req_map |= req; + else + bp->pps_req_map &= ~req; + + /* if no state change, just return */ + if ((!!old_map ^ !!bp->pps_req_map) == 0) + return 0; + } if (enable) { iowrite32(1, ®->enable); @@ -1033,7 +1288,7 @@ ptp_ocp_ts_enable(void *priv, bool enable) static void ptp_ocp_unregister_ext(struct ptp_ocp_ext_src *ext) { - ext->info->enable(ext, false); + ext->info->enable(ext, ~0, false); pci_free_irq(ext->bp->pdev, ext->irq_vec, ext); kfree(ext); } @@ -1049,17 +1304,18 @@ ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r) if (!ext) return -ENOMEM; - err = -EINVAL; ext->mem = ptp_ocp_get_mem(bp, r); - if (!ext->mem) + if (IS_ERR(ext->mem)) { + err = PTR_ERR(ext->mem); goto out; + } ext->bp = bp; ext->info = r->extra; ext->irq_vec = r->irq_vec; err = pci_request_irq(pdev, r->irq_vec, ext->info->irq_fcn, NULL, - ext, "ocp%d.%s", bp->id, ext->info->name); + ext, "ocp%d.%s", bp->id, r->name); if (err) { dev_err(&pdev->dev, "Could not get irq %d\n", r->irq_vec); goto out; @@ -1101,12 +1357,6 @@ ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r) { int port; - if (r->irq_vec > bp->n_irqs) { - dev_err(&bp->pdev->dev, "serial device irq %d out of range\n", - r->irq_vec); - return 0; - } - port = ptp_ocp_serial_line(bp, r); if (port < 0) return port; @@ -1122,23 +1372,48 @@ ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r) void __iomem *mem; mem = ptp_ocp_get_mem(bp, r); - if (!mem) - return -EINVAL; + if (IS_ERR(mem)) + return PTR_ERR(mem); bp_assign_entry(bp, r, mem); return 0; } +static void +ptp_ocp_nmea_out_init(struct ptp_ocp *bp) +{ + if (!bp->nmea_out) + return; + + iowrite32(0, &bp->nmea_out->ctrl); /* disable */ + iowrite32(7, &bp->nmea_out->uart_baud); /* 115200 */ + iowrite32(1, &bp->nmea_out->ctrl); /* enable */ +} + /* FB specific board initializers; last "resource" registered. */ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) { bp->flash_start = 1024 * 4096; + ptp_ocp_tod_init(bp); + ptp_ocp_nmea_out_init(bp); + return ptp_ocp_init_clock(bp); } +static bool +ptp_ocp_allow_irq(struct ptp_ocp *bp, struct ocp_resource *r) +{ + bool allow = !r->irq_vec || r->irq_vec < bp->n_irqs; + + if (!allow) + dev_err(&bp->pdev->dev, "irq %d out of range, skipping %s\n", + r->irq_vec, r->name); + return allow; +} + static int ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) { @@ -1147,13 +1422,373 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) table = (struct ocp_resource *)driver_data; for (r = table; r->setup; r++) { + if (!ptp_ocp_allow_irq(bp, r)) + continue; err = r->setup(bp, r); - if (err) + if (err) { + dev_err(&bp->pdev->dev, + "Could not register %s: err %d\n", + r->name, err); break; + } } return err; } +static void +ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable) +{ + u32 ctrl; + bool on; + + ctrl = ioread32(reg); + on = ctrl & bit; + if (on ^ enable) { + ctrl &= ~bit; + ctrl |= enable ? bit : 0; + iowrite32(ctrl, reg); + } +} + +static void +ptp_ocp_irig_out(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->irig_out->ctrl, + IRIG_M_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_irig_in(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->irig_in->ctrl, + IRIG_S_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_dcf_out(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->dcf_out->ctrl, + DCF_M_CTRL_ENABLE, enable); +} + +static void +ptp_ocp_dcf_in(struct ptp_ocp *bp, bool enable) +{ + return ptp_ocp_enable_fpga(&bp->dcf_in->ctrl, + DCF_S_CTRL_ENABLE, enable); +} + +static void +__handle_signal_outputs(struct ptp_ocp *bp, u32 val) +{ + ptp_ocp_irig_out(bp, val & 0x00100010); + ptp_ocp_dcf_out(bp, val & 0x00200020); +} + +static void +__handle_signal_inputs(struct ptp_ocp *bp, u32 val) +{ + ptp_ocp_irig_in(bp, val & 0x00100010); + ptp_ocp_dcf_in(bp, val & 0x00200020); +} + +/* + * ANT0 == gps (in) + * ANT1 == sma1 (in) + * ANT2 == sma2 (in) + * ANT3 == sma3 (out) + * ANT4 == sma4 (out) + */ + +enum ptp_ocp_sma_mode { + SMA_MODE_IN, + SMA_MODE_OUT, +}; + +static struct ptp_ocp_sma_connector { + enum ptp_ocp_sma_mode mode; + bool fixed_mode; + u16 default_out_idx; +} ptp_ocp_sma_map[4] = { + { + .mode = SMA_MODE_IN, + .fixed_mode = true, + }, + { + .mode = SMA_MODE_IN, + .fixed_mode = true, + }, + { + .mode = SMA_MODE_OUT, + .fixed_mode = true, + .default_out_idx = 0, /* 10Mhz */ + }, + { + .mode = SMA_MODE_OUT, + .fixed_mode = true, + .default_out_idx = 1, /* PHC */ + }, +}; + +static ssize_t +ptp_ocp_show_output(u32 val, char *buf, int default_idx) +{ + const char *name; + ssize_t count; + + count = sysfs_emit(buf, "OUT: "); + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val); + if (!name) + name = ptp_ocp_sma_out[default_idx].name; + count += sysfs_emit_at(buf, count, "%s\n", name); + return count; +} + +static ssize_t +ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) +{ + const char *name; + ssize_t count; + int i; + + count = sysfs_emit(buf, "IN: "); + for (i = 0; i < ARRAY_SIZE(ptp_ocp_sma_in); i++) { + if (val & ptp_ocp_sma_in[i].value) { + name = ptp_ocp_sma_in[i].name; + count += sysfs_emit_at(buf, count, "%s ", name); + } + } + if (!val && zero_in) + count += sysfs_emit_at(buf, count, "%s ", zero_in); + if (count) + count--; + count += sysfs_emit_at(buf, count, "\n"); + return count; +} + +static int +sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode) +{ + struct ocp_selector *tbl[] = { ptp_ocp_sma_in, ptp_ocp_sma_out }; + int idx, count, dir; + char **argv; + int ret; + + argv = argv_split(GFP_KERNEL, buf, &count); + if (!argv) + return -ENOMEM; + + ret = -EINVAL; + if (!count) + goto out; + + idx = 0; + dir = *mode == SMA_MODE_IN ? 0 : 1; + if (!strcasecmp("IN:", argv[idx])) { + dir = 0; + idx++; + } + if (!strcasecmp("OUT:", argv[0])) { + dir = 1; + idx++; + } + *mode = dir == 0 ? SMA_MODE_IN : SMA_MODE_OUT; + + ret = 0; + for (; idx < count; idx++) + ret |= ptp_ocp_select_val_from_name(tbl[dir], argv[idx]); + if (ret < 0) + ret = -EINVAL; + +out: + argv_free(argv); + return ret; +} + +static ssize_t +ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf, + const char *zero_in) +{ + struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + + if (sma->mode == SMA_MODE_IN) + return ptp_ocp_show_inputs(val, buf, zero_in); + + return ptp_ocp_show_output(val, buf, sma->default_out_idx); +} + +static ssize_t +sma1_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->sma->gpio1) & 0x3f; + return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name); +} + +static ssize_t +sma2_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f; + return ptp_ocp_sma_show(bp, 2, val, buf, NULL); +} + +static ssize_t +sma3_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->sma->gpio2) & 0x3f; + return ptp_ocp_sma_show(bp, 3, val, buf, NULL); +} + +static ssize_t +sma4_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f; + return ptp_ocp_sma_show(bp, 4, val, buf, NULL); +} + +static void +ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift) +{ + unsigned long flags; + u32 gpio, mask; + + mask = 0xffff << (16 - shift); + + spin_lock_irqsave(&bp->lock, flags); + + gpio = ioread32(&bp->sma->gpio2); + gpio = (gpio & mask) | (val << shift); + + __handle_signal_outputs(bp, gpio); + + iowrite32(gpio, &bp->sma->gpio2); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void +ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift) +{ + unsigned long flags; + u32 gpio, mask; + + mask = 0xffff << (16 - shift); + + spin_lock_irqsave(&bp->lock, flags); + + gpio = ioread32(&bp->sma->gpio1); + gpio = (gpio & mask) | (val << shift); + + __handle_signal_inputs(bp, gpio); + + iowrite32(gpio, &bp->sma->gpio1); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static ssize_t +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) +{ + struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + enum ptp_ocp_sma_mode mode; + int val; + + mode = sma->mode; + val = sma_parse_inputs(buf, &mode); + if (val < 0) + return val; + + if (mode != sma->mode && sma->fixed_mode) + return -EOPNOTSUPP; + + if (mode != sma->mode) { + pr_err("Mode changes not supported yet.\n"); + return -EOPNOTSUPP; + } + + if (sma->mode == SMA_MODE_IN) + ptp_ocp_sma_store_inputs(bp, val, shift); + else + ptp_ocp_sma_store_output(bp, val, shift); + + return 0; +} + +static ssize_t +sma1_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 1, 0); + return err ? err : count; +} + +static ssize_t +sma2_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 2, 16); + return err ? err : count; +} + +static ssize_t +sma3_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 3, 0); + return err ? err : count; +} + +static ssize_t +sma4_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + + err = ptp_ocp_sma_store(bp, buf, 4, 16); + return err ? err : count; +} +static DEVICE_ATTR_RW(sma1); +static DEVICE_ATTR_RW(sma2); +static DEVICE_ATTR_RW(sma3); +static DEVICE_ATTR_RW(sma4); + +static ssize_t +available_sma_inputs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return ptp_ocp_select_table_show(ptp_ocp_sma_in, buf); +} +static DEVICE_ATTR_RO(available_sma_inputs); + +static ssize_t +available_sma_outputs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return ptp_ocp_select_table_show(ptp_ocp_sma_out, buf); +} +static DEVICE_ATTR_RO(available_sma_outputs); + static ssize_t serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1182,6 +1817,102 @@ gnss_sync_show(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR_RO(gnss_sync); static ssize_t +utc_tai_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", bp->utc_tai_offset); +} + +static ssize_t +utc_tai_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + u32 val; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + ptp_ocp_utc_distribute(bp, val); + + return count; +} +static DEVICE_ATTR_RW(utc_tai_offset); + +static ssize_t +ts_window_adjust_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", bp->ts_window_adjust); +} + +static ssize_t +ts_window_adjust_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int err; + u32 val; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + bp->ts_window_adjust = val; + + return count; +} +static DEVICE_ATTR_RW(ts_window_adjust); + +static ssize_t +irig_b_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + + val = ioread32(&bp->irig_out->ctrl); + val = (val >> 16) & 0x07; + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t +irig_b_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + unsigned long flags; + int err; + u32 reg; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err) + return err; + if (val > 7) + return -EINVAL; + + reg = ((val & 0x7) << 16); + + spin_lock_irqsave(&bp->lock, flags); + iowrite32(0, &bp->irig_out->ctrl); /* disable */ + iowrite32(reg, &bp->irig_out->ctrl); /* change mode */ + iowrite32(reg | IRIG_M_CTRL_ENABLE, &bp->irig_out->ctrl); + spin_unlock_irqrestore(&bp->lock, flags); + + return count; +} +static DEVICE_ATTR_RW(irig_b_mode); + +static ssize_t clock_source_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); @@ -1189,7 +1920,7 @@ clock_source_show(struct device *dev, struct device_attribute *attr, char *buf) u32 select; select = ioread32(&bp->reg->select); - p = ptp_ocp_clock_name_from_val(select >> 16); + p = ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16); return sysfs_emit(buf, "%s\n", p); } @@ -1202,7 +1933,7 @@ clock_source_store(struct device *dev, struct device_attribute *attr, unsigned long flags; int val; - val = ptp_ocp_clock_val_from_name(buf); + val = ptp_ocp_select_val_from_name(ptp_ocp_clock, buf); if (val < 0) return val; @@ -1218,19 +1949,7 @@ static ssize_t available_clock_sources_show(struct device *dev, struct device_attribute *attr, char *buf) { - const char *clk; - ssize_t count; - int i; - - count = 0; - for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) { - clk = ptp_ocp_clock[i].name; - count += sysfs_emit_at(buf, count, "%s ", clk); - } - if (count) - count--; - count += sysfs_emit_at(buf, count, "\n"); - return count; + return ptp_ocp_select_table_show(ptp_ocp_clock, buf); } static DEVICE_ATTR_RO(available_clock_sources); @@ -1239,10 +1958,258 @@ static struct attribute *timecard_attrs[] = { &dev_attr_gnss_sync.attr, &dev_attr_clock_source.attr, &dev_attr_available_clock_sources.attr, + &dev_attr_sma1.attr, + &dev_attr_sma2.attr, + &dev_attr_sma3.attr, + &dev_attr_sma4.attr, + &dev_attr_available_sma_inputs.attr, + &dev_attr_available_sma_outputs.attr, + &dev_attr_irig_b_mode.attr, + &dev_attr_utc_tai_offset.attr, + &dev_attr_ts_window_adjust.attr, NULL, }; ATTRIBUTE_GROUPS(timecard); +static const char * +gpio_map(u32 gpio, u32 bit, const char *pri, const char *sec, const char *def) +{ + const char *ans; + + if (gpio & (1 << bit)) + ans = pri; + else if (gpio & (1 << (bit + 16))) + ans = sec; + else + ans = def; + return ans; +} + +static void +gpio_multi_map(char *buf, u32 gpio, u32 bit, + const char *pri, const char *sec, const char *def) +{ + char *ans = buf; + + strcpy(ans, def); + if (gpio & (1 << bit)) + ans += sprintf(ans, "%s ", pri); + if (gpio & (1 << (bit + 16))) + ans += sprintf(ans, "%s ", sec); +} + +static int +ptp_ocp_summary_show(struct seq_file *s, void *data) +{ + struct device *dev = s->private; + struct ptp_system_timestamp sts; + u32 sma_in, sma_out, ctrl, val; + struct ts_reg __iomem *ts_reg; + struct timespec64 ts; + struct ptp_ocp *bp; + const char *src; + bool on, map; + char *buf; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + bp = dev_get_drvdata(dev); + sma_in = ioread32(&bp->sma->gpio1); + sma_out = ioread32(&bp->sma->gpio2); + + seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp)); + + sma1_show(dev, NULL, buf); + seq_printf(s, " sma1: %s", buf); + + sma2_show(dev, NULL, buf); + seq_printf(s, " sma2: %s", buf); + + sma3_show(dev, NULL, buf); + seq_printf(s, " sma3: %s", buf); + + sma4_show(dev, NULL, buf); + seq_printf(s, " sma4: %s", buf); + + if (bp->ts0) { + ts_reg = bp->ts0->mem; + on = ioread32(&ts_reg->enable); + src = "GNSS"; + seq_printf(s, "%7s: %s, src: %s\n", "TS0", + on ? " ON" : "OFF", src); + } + + if (bp->ts1) { + ts_reg = bp->ts1->mem; + on = ioread32(&ts_reg->enable); + src = gpio_map(sma_in, 2, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, src: %s\n", "TS1", + on ? " ON" : "OFF", src); + } + + if (bp->ts2) { + ts_reg = bp->ts2->mem; + on = ioread32(&ts_reg->enable); + src = gpio_map(sma_in, 3, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, src: %s\n", "TS2", + on ? " ON" : "OFF", src); + } + + if (bp->pps) { + ts_reg = bp->pps->mem; + src = "PHC"; + on = ioread32(&ts_reg->enable); + map = !!(bp->pps_req_map & OCP_REQ_TIMESTAMP); + seq_printf(s, "%7s: %s, src: %s\n", "TS3", + on && map ? " ON" : "OFF", src); + + map = !!(bp->pps_req_map & OCP_REQ_PPS); + seq_printf(s, "%7s: %s, src: %s\n", "PPS", + on && map ? " ON" : "OFF", src); + } + + if (bp->irig_out) { + ctrl = ioread32(&bp->irig_out->ctrl); + on = ctrl & IRIG_M_CTRL_ENABLE; + val = ioread32(&bp->irig_out->status); + gpio_multi_map(buf, sma_out, 4, "sma3", "sma4", "----"); + seq_printf(s, "%7s: %s, error: %d, mode %d, out: %s\n", "IRIG", + on ? " ON" : "OFF", val, (ctrl >> 16), buf); + } + + if (bp->irig_in) { + on = ioread32(&bp->irig_in->ctrl) & IRIG_S_CTRL_ENABLE; + val = ioread32(&bp->irig_in->status); + src = gpio_map(sma_in, 4, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, error: %d, src: %s\n", "IRIG in", + on ? " ON" : "OFF", val, src); + } + + if (bp->dcf_out) { + on = ioread32(&bp->dcf_out->ctrl) & DCF_M_CTRL_ENABLE; + val = ioread32(&bp->dcf_out->status); + gpio_multi_map(buf, sma_out, 5, "sma3", "sma4", "----"); + seq_printf(s, "%7s: %s, error: %d, out: %s\n", "DCF", + on ? " ON" : "OFF", val, buf); + } + + if (bp->dcf_in) { + on = ioread32(&bp->dcf_in->ctrl) & DCF_S_CTRL_ENABLE; + val = ioread32(&bp->dcf_in->status); + src = gpio_map(sma_in, 5, "sma1", "sma2", "----"); + seq_printf(s, "%7s: %s, error: %d, src: %s\n", "DCF in", + on ? " ON" : "OFF", val, src); + } + + if (bp->nmea_out) { + on = ioread32(&bp->nmea_out->ctrl) & 1; + val = ioread32(&bp->nmea_out->status); + seq_printf(s, "%7s: %s, error: %d\n", "NMEA", + on ? " ON" : "OFF", val); + } + + /* compute src for PPS1, used below. */ + if (bp->pps_select) { + val = ioread32(&bp->pps_select->gpio1); + if (val & 0x01) + src = gpio_map(sma_in, 0, "sma1", "sma2", "----"); + else if (val & 0x02) + src = "MAC"; + else if (val & 0x04) + src = "GNSS"; + else + src = "----"; + } else { + src = "?"; + } + + /* assumes automatic switchover/selection */ + val = ioread32(&bp->reg->select); + switch (val >> 16) { + case 0: + sprintf(buf, "----"); + break; + case 2: + sprintf(buf, "IRIG"); + break; + case 3: + sprintf(buf, "%s via PPS1", src); + break; + case 6: + sprintf(buf, "DCF"); + break; + default: + strcpy(buf, "unknown"); + break; + } + val = ioread32(&bp->reg->status); + seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf, + val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced"); + + /* reuses PPS1 src from earlier */ + seq_printf(s, "MAC PPS1 src: %s\n", src); + + src = gpio_map(sma_in, 1, "sma1", "sma2", "GNSS2"); + seq_printf(s, "MAC PPS2 src: %s\n", src); + + if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) { + struct timespec64 sys_ts; + s64 pre_ns, post_ns, ns; + + pre_ns = timespec64_to_ns(&sts.pre_ts); + post_ns = timespec64_to_ns(&sts.post_ts); + ns = (pre_ns + post_ns) / 2; + ns += (s64)bp->utc_tai_offset * NSEC_PER_SEC; + sys_ts = ns_to_timespec64(ns); + + seq_printf(s, "%7s: %lld.%ld == %ptT TAI\n", "PHC", + ts.tv_sec, ts.tv_nsec, &ts); + seq_printf(s, "%7s: %lld.%ld == %ptT UTC offset %d\n", "SYS", + sys_ts.tv_sec, sys_ts.tv_nsec, &sys_ts, + bp->utc_tai_offset); + seq_printf(s, "%7s: PHC:SYS offset: %lld window: %lld\n", "", + timespec64_to_ns(&ts) - ns, + post_ns - pre_ns); + } + + free_page((unsigned long)buf); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary); + +static struct dentry *ptp_ocp_debugfs_root; + +static void +ptp_ocp_debugfs_add_device(struct ptp_ocp *bp) +{ + struct dentry *d; + + d = debugfs_create_dir(dev_name(&bp->dev), ptp_ocp_debugfs_root); + bp->debug_root = d; + debugfs_create_file("summary", 0444, bp->debug_root, + &bp->dev, &ptp_ocp_summary_fops); +} + +static void +ptp_ocp_debugfs_remove_device(struct ptp_ocp *bp) +{ + debugfs_remove_recursive(bp->debug_root); +} + +static void +ptp_ocp_debugfs_init(void) +{ + ptp_ocp_debugfs_root = debugfs_create_dir("timecard", NULL); +} + +static void +ptp_ocp_debugfs_fini(void) +{ + debugfs_remove_recursive(ptp_ocp_debugfs_root); +} + static void ptp_ocp_dev_release(struct device *dev) { @@ -1270,7 +2237,9 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) bp->ptp_info = ptp_ocp_clock_info; spin_lock_init(&bp->lock); bp->gnss_port = -1; + bp->gnss2_port = -1; bp->mac_port = -1; + bp->nmea_port = -1; bp->pdev = pdev; device_initialize(&bp->dev); @@ -1332,10 +2301,18 @@ ptp_ocp_complete(struct ptp_ocp *bp) sprintf(buf, "ttyS%d", bp->gnss_port); ptp_ocp_link_child(bp, buf, "ttyGNSS"); } + if (bp->gnss2_port != -1) { + sprintf(buf, "ttyS%d", bp->gnss2_port); + ptp_ocp_link_child(bp, buf, "ttyGNSS2"); + } if (bp->mac_port != -1) { sprintf(buf, "ttyS%d", bp->mac_port); ptp_ocp_link_child(bp, buf, "ttyMAC"); } + if (bp->nmea_port != -1) { + sprintf(buf, "ttyS%d", bp->nmea_port); + ptp_ocp_link_child(bp, buf, "ttyNMEA"); + } sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp)); ptp_ocp_link_child(bp, buf, "ptp"); @@ -1346,13 +2323,53 @@ ptp_ocp_complete(struct ptp_ocp *bp) if (device_add_groups(&bp->dev, timecard_groups)) pr_err("device add groups failed\n"); + ptp_ocp_debugfs_add_device(bp); + return 0; } static void -ptp_ocp_resource_summary(struct ptp_ocp *bp) +ptp_ocp_phc_info(struct ptp_ocp *bp) { + struct timespec64 ts; + u32 version, select; + bool sync; + + version = ioread32(&bp->reg->version); + select = ioread32(&bp->reg->select); + dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n", + version >> 24, (version >> 16) & 0xff, version & 0xffff, + ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16), + ptp_clock_index(bp->ptp)); + + sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; + if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL)) + dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n", + ts.tv_sec, ts.tv_nsec, + sync ? "in-sync" : "UNSYNCED"); +} + +static void +ptp_ocp_serial_info(struct device *dev, const char *name, int port, int baud) +{ + if (port != -1) + dev_info(dev, "%5s: /dev/ttyS%-2d @ %6d\n", name, port, baud); +} + +static void +ptp_ocp_info(struct ptp_ocp *bp) +{ + static int nmea_baud[] = { + 1200, 2400, 4800, 9600, 19200, 38400, + 57600, 115200, 230400, 460800, 921600, + 1000000, 2000000 + }; struct device *dev = &bp->pdev->dev; + u32 reg; + + ptp_ocp_phc_info(bp); + if (bp->tod) + ptp_ocp_tod_info(bp); if (bp->image) { u32 ver = ioread32(&bp->image->version); @@ -1365,10 +2382,17 @@ ptp_ocp_resource_summary(struct ptp_ocp *bp) dev_info(dev, "golden image, version %d\n", ver >> 16); } - if (bp->gnss_port != -1) - dev_info(dev, "GNSS @ /dev/ttyS%d 115200\n", bp->gnss_port); - if (bp->mac_port != -1) - dev_info(dev, "MAC @ /dev/ttyS%d 57600\n", bp->mac_port); + ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); + ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200); + ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); + if (bp->nmea_out && bp->nmea_port != -1) { + int baud = -1; + + reg = ioread32(&bp->nmea_out->uart_baud); + if (reg < ARRAY_SIZE(nmea_baud)) + baud = nmea_baud[reg]; + ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud); + } } static void @@ -1386,6 +2410,7 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp) static void ptp_ocp_detach(struct ptp_ocp *bp) { + ptp_ocp_debugfs_remove_device(bp); ptp_ocp_detach_sysfs(bp); if (timer_pending(&bp->watchdog)) del_timer_sync(&bp->watchdog); @@ -1393,12 +2418,18 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_unregister_ext(bp->ts0); if (bp->ts1) ptp_ocp_unregister_ext(bp->ts1); + if (bp->ts2) + ptp_ocp_unregister_ext(bp->ts2); if (bp->pps) ptp_ocp_unregister_ext(bp->pps); if (bp->gnss_port != -1) serial8250_unregister_port(bp->gnss_port); + if (bp->gnss2_port != -1) + serial8250_unregister_port(bp->gnss2_port); if (bp->mac_port != -1) serial8250_unregister_port(bp->mac_port); + if (bp->nmea_port != -1) + serial8250_unregister_port(bp->nmea_port); if (bp->spi_flash) platform_device_unregister(bp->spi_flash); if (bp->i2c_ctrl) @@ -1425,10 +2456,6 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; } - err = devlink_register(devlink); - if (err) - goto out_free; - err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "pci_enable_device\n"); @@ -1445,7 +2472,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) * allow this - if not all of the IRQ's are returned, skip the * extra devices and just register the clock. */ - err = pci_alloc_irq_vectors(pdev, 1, 10, PCI_IRQ_MSI | PCI_IRQ_MSIX); + err = pci_alloc_irq_vectors(pdev, 1, 11, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (err < 0) { dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err); goto out; @@ -1470,8 +2497,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out; ptp_ocp_info(bp); - ptp_ocp_resource_summary(bp); - + devlink_register(devlink); return 0; out: @@ -1480,10 +2506,7 @@ out: out_disable: pci_disable_device(pdev); out_unregister: - devlink_unregister(devlink); -out_free: devlink_free(devlink); - return err; } @@ -1493,11 +2516,11 @@ ptp_ocp_remove(struct pci_dev *pdev) struct ptp_ocp *bp = pci_get_drvdata(pdev); struct devlink *devlink = priv_to_devlink(bp); + devlink_unregister(devlink); ptp_ocp_detach(bp); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); - devlink_unregister(devlink); devlink_free(devlink); } @@ -1554,6 +2577,8 @@ ptp_ocp_init(void) const char *what; int err; + ptp_ocp_debugfs_init(); + what = "timecard class"; err = class_register(&timecard_class); if (err) @@ -1576,6 +2601,7 @@ out_register: out_notifier: class_unregister(&timecard_class); out: + ptp_ocp_debugfs_fini(); pr_err(KBUILD_MODNAME ": failed to register %s: %d\n", what, err); return err; } @@ -1586,6 +2612,7 @@ ptp_ocp_fini(void) bus_unregister_notifier(&i2c_bus_type, &ptp_ocp_i2c_notifier); pci_unregister_driver(&ptp_ocp_driver); class_unregister(&timecard_class); + ptp_ocp_debugfs_fini(); } module_init(ptp_ocp_init); |