2296 lines
76 KiB
C
2296 lines
76 KiB
C
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
|
/*
|
|
* Apple Type-C PHY driver
|
|
*
|
|
* The Apple Type-C PHY (ATCPHY) is a combined PHY for USB 2.0, USB 3.x,
|
|
* USB4/Thunderbolt, and DisplayPort connectivity via Type-C ports found in
|
|
* Apple Silicon SoCs.
|
|
*
|
|
* The PHY handles muxing between these different protocols and also provides the
|
|
* reset controller for the attached DWC3 USB controller.
|
|
*
|
|
* No documentation for this PHY is available and its operation has been
|
|
* reverse engineered by observing the XNU's MMIO access using a thin hypervisor
|
|
* and correlating register access to XNU's very verbose debug output. Most
|
|
* register names comes from this debug output as well.
|
|
*
|
|
* In order to correctly setup the high speed lanes for the various modes
|
|
* calibration values copied from Apple's firmware by our bootloader m1n1 are
|
|
* required. Without these only USB2 operation is possible.
|
|
*
|
|
* Copyright (C) The Asahi Linux Contributors
|
|
* Author: Sven Peter <sven@kernel.org>
|
|
*/
|
|
|
|
#include <dt-bindings/phy/phy.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/phy/phy.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/soc/apple/tunable.h>
|
|
#include <linux/types.h>
|
|
#include <linux/usb/pd.h>
|
|
#include <linux/usb/typec.h>
|
|
#include <linux/usb/typec_altmode.h>
|
|
#include <linux/usb/typec_dp.h>
|
|
#include <linux/usb/typec_mux.h>
|
|
#include <linux/usb/typec_tbt.h>
|
|
|
|
#define AUSPLL_FSM_CTRL 0x1014
|
|
|
|
#define AUSPLL_APB_CMD_OVERRIDE 0x2000
|
|
#define AUSPLL_APB_CMD_OVERRIDE_REQ BIT(0)
|
|
#define AUSPLL_APB_CMD_OVERRIDE_ACK BIT(1)
|
|
#define AUSPLL_APB_CMD_OVERRIDE_UNK28 BIT(28)
|
|
#define AUSPLL_APB_CMD_OVERRIDE_CMD GENMASK(27, 3)
|
|
|
|
#define AUSPLL_FREQ_DESC_A 0x2080
|
|
#define AUSPLL_FD_FREQ_COUNT_TARGET GENMASK(9, 0)
|
|
#define AUSPLL_FD_FBDIVN_HALF BIT(10)
|
|
#define AUSPLL_FD_REV_DIVN GENMASK(13, 11)
|
|
#define AUSPLL_FD_KI_MAN GENMASK(17, 14)
|
|
#define AUSPLL_FD_KI_EXP GENMASK(21, 18)
|
|
#define AUSPLL_FD_KP_MAN GENMASK(25, 22)
|
|
#define AUSPLL_FD_KP_EXP GENMASK(29, 26)
|
|
#define AUSPLL_FD_KPKI_SCALE_HBW GENMASK(31, 30)
|
|
|
|
#define AUSPLL_FREQ_DESC_B 0x2084
|
|
#define AUSPLL_FD_FBDIVN_FRAC_DEN GENMASK(13, 0)
|
|
#define AUSPLL_FD_FBDIVN_FRAC_NUM GENMASK(27, 14)
|
|
|
|
#define AUSPLL_FREQ_DESC_C 0x2088
|
|
#define AUSPLL_FD_SDM_SSC_STEP GENMASK(7, 0)
|
|
#define AUSPLL_FD_SDM_SSC_EN BIT(8)
|
|
#define AUSPLL_FD_PCLK_DIV_SEL GENMASK(13, 9)
|
|
#define AUSPLL_FD_LFSDM_DIV GENMASK(15, 14)
|
|
#define AUSPLL_FD_LFCLK_CTRL GENMASK(19, 16)
|
|
#define AUSPLL_FD_VCLK_OP_DIVN GENMASK(21, 20)
|
|
#define AUSPLL_FD_VCLK_PRE_DIVN BIT(22)
|
|
|
|
#define AUSPLL_DCO_EFUSE_SPARE 0x222c
|
|
#define AUSPLL_RODCO_ENCAP_EFUSE GENMASK(10, 9)
|
|
#define AUSPLL_RODCO_BIAS_ADJUST_EFUSE GENMASK(14, 12)
|
|
|
|
#define AUSPLL_FRACN_CAN 0x22a4
|
|
#define AUSPLL_DLL_START_CAPCODE GENMASK(18, 17)
|
|
|
|
#define AUSPLL_CLKOUT_MASTER 0x2200
|
|
#define AUSPLL_CLKOUT_MASTER_PCLK_DRVR_EN BIT(2)
|
|
#define AUSPLL_CLKOUT_MASTER_PCLK2_DRVR_EN BIT(4)
|
|
#define AUSPLL_CLKOUT_MASTER_REFBUFCLK_DRVR_EN BIT(6)
|
|
|
|
#define AUSPLL_CLKOUT_DIV 0x2208
|
|
#define AUSPLL_CLKOUT_PLLA_REFBUFCLK_DI GENMASK(20, 16)
|
|
|
|
#define AUSPLL_BGR 0x2214
|
|
#define AUSPLL_BGR_CTRL_AVAIL BIT(0)
|
|
|
|
#define AUSPLL_CLKOUT_DTC_VREG 0x2220
|
|
#define AUSPLL_DTC_VREG_ADJUST GENMASK(16, 14)
|
|
#define AUSPLL_DTC_VREG_BYPASS BIT(7)
|
|
|
|
#define AUSPLL_FREQ_CFG 0x2224
|
|
#define AUSPLL_FREQ_REFCLK GENMASK(1, 0)
|
|
|
|
#define AUS_COMMON_SHIM_BLK_VREG 0x0a04
|
|
#define AUS_VREG_TRIM GENMASK(6, 2)
|
|
|
|
#define AUS_UNK_A20 0x0a20
|
|
#define AUS_UNK_A20_TX_CAL_CODE GENMASK(23, 20)
|
|
|
|
#define ACIOPHY_CMN_SHM_STS_REG0 0x0a74
|
|
#define ACIOPHY_CMN_SHM_STS_REG0_CMD_READY BIT(0)
|
|
|
|
#define CIO3PLL_CLK_CTRL 0x2a00
|
|
#define CIO3PLL_CLK_PCLK_EN BIT(1)
|
|
#define CIO3PLL_CLK_REFCLK_EN BIT(5)
|
|
|
|
#define CIO3PLL_DCO_NCTRL 0x2a38
|
|
#define CIO3PLL_DCO_COARSEBIN_EFUSE0 GENMASK(6, 0)
|
|
#define CIO3PLL_DCO_COARSEBIN_EFUSE1 GENMASK(23, 17)
|
|
|
|
#define CIO3PLL_FRACN_CAN 0x2aa4
|
|
#define CIO3PLL_DLL_CAL_START_CAPCODE GENMASK(18, 17)
|
|
|
|
#define CIO3PLL_DTC_VREG 0x2a20
|
|
#define CIO3PLL_DTC_VREG_ADJUST GENMASK(16, 14)
|
|
|
|
#define ACIOPHY_CFG0 0x08
|
|
#define ACIOPHY_CFG0_COMMON_BIG_OV BIT(1)
|
|
#define ACIOPHY_CFG0_COMMON_SMALL_OV BIT(3)
|
|
#define ACIOPHY_CFG0_COMMON_CLAMP_OV BIT(5)
|
|
#define ACIOPHY_CFG0_RX_SMALL_OV GENMASK(9, 8)
|
|
#define ACIOPHY_CFG0_RX_BIG_OV GENMASK(13, 12)
|
|
#define ACIOPHY_CFG0_RX_CLAMP_OV GENMASK(17, 16)
|
|
|
|
#define ACIOPHY_CROSSBAR 0x4c
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL GENMASK(4, 0)
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB4 0x0
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB4_SWAPPED 0x1
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB3 0xa
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB3_SWAPPED 0xb
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP 0x10
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP_SWAPPED 0x11
|
|
#define ACIOPHY_CROSSBAR_PROTOCOL_DP 0x14
|
|
#define ACIOPHY_CROSSBAR_DP_SINGLE_PMA GENMASK(16, 5)
|
|
#define ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE 0x0000
|
|
#define ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK100 0x100
|
|
#define ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008 0x008
|
|
#define ACIOPHY_CROSSBAR_DP_BOTH_PMA BIT(17)
|
|
|
|
#define ACIOPHY_LANE_MODE 0x48
|
|
#define ACIOPHY_LANE_MODE_RX0 GENMASK(2, 0)
|
|
#define ACIOPHY_LANE_MODE_TX0 GENMASK(5, 3)
|
|
#define ACIOPHY_LANE_MODE_RX1 GENMASK(8, 6)
|
|
#define ACIOPHY_LANE_MODE_TX1 GENMASK(11, 9)
|
|
|
|
enum atcphy_lane_mode {
|
|
ACIOPHY_LANE_MODE_USB4 = 0,
|
|
ACIOPHY_LANE_MODE_USB3 = 1,
|
|
ACIOPHY_LANE_MODE_DP = 2,
|
|
ACIOPHY_LANE_MODE_OFF = 3,
|
|
};
|
|
|
|
#define ACIOPHY_TOP_BIST_CIOPHY_CFG1 0x84
|
|
#define ACIOPHY_TOP_BIST_CIOPHY_CFG1_CLK_EN BIT(27)
|
|
#define ACIOPHY_TOP_BIST_CIOPHY_CFG1_BIST_EN BIT(28)
|
|
|
|
#define ACIOPHY_TOP_BIST_OV_CFG 0x8c
|
|
#define ACIOPHY_TOP_BIST_OV_CFG_LN0_RESET_N_OV BIT(13)
|
|
#define ACIOPHY_TOP_BIST_OV_CFG_LN0_PWR_DOWN_OV BIT(25)
|
|
|
|
#define ACIOPHY_TOP_BIST_READ_CTRL 0x90
|
|
#define ACIOPHY_TOP_BIST_READ_CTRL_LN0_PHY_STATUS_RE BIT(2)
|
|
|
|
#define ACIOPHY_TOP_PHY_STAT 0x9c
|
|
#define ACIOPHY_TOP_PHY_STAT_LN0_UNK0 BIT(0)
|
|
#define ACIOPHY_TOP_PHY_STAT_LN0_UNK23 BIT(23)
|
|
|
|
#define ACIOPHY_TOP_BIST_PHY_CFG0 0xa8
|
|
#define ACIOPHY_TOP_BIST_PHY_CFG0_LN0_RESET_N BIT(0)
|
|
|
|
#define ACIOPHY_TOP_BIST_PHY_CFG1 0xac
|
|
#define ACIOPHY_TOP_BIST_PHY_CFG1_LN0_PWR_DOWN GENMASK(13, 10)
|
|
|
|
#define ACIOPHY_SLEEP_CTRL 0x1b0
|
|
#define ACIOPHY_SLEEP_CTRL_TX_BIG_OV GENMASK(3, 2)
|
|
#define ACIOPHY_SLEEP_CTRL_TX_SMALL_OV GENMASK(7, 6)
|
|
#define ACIOPHY_SLEEP_CTRL_TX_CLAMP_OV GENMASK(11, 10)
|
|
|
|
#define ACIOPHY_PLL_PCTL_FSM_CTRL1 0x1014
|
|
#define ACIOPHY_PLL_APB_REQ_OV_SEL GENMASK(21, 13)
|
|
#define ACIOPHY_PLL_COMMON_CTRL 0x1028
|
|
#define ACIOPHY_PLL_WAIT_FOR_CMN_READY_BEFORE_RESET_EXIT BIT(24)
|
|
|
|
#define ATCPHY_POWER_CTRL 0x20000
|
|
#define ATCPHY_POWER_STAT 0x20004
|
|
#define ATCPHY_POWER_SLEEP_SMALL BIT(0)
|
|
#define ATCPHY_POWER_SLEEP_BIG BIT(1)
|
|
#define ATCPHY_POWER_CLAMP_EN BIT(2)
|
|
#define ATCPHY_POWER_APB_RESET_N BIT(3)
|
|
#define ATCPHY_POWER_PHY_RESET_N BIT(4)
|
|
|
|
#define ATCPHY_MISC 0x20008
|
|
#define ATCPHY_MISC_RESET_N BIT(0)
|
|
#define ATCPHY_MISC_LANE_SWAP BIT(2)
|
|
|
|
#define ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 0x7000
|
|
#define DP_PMA_BYTECLK_RESET BIT(0)
|
|
#define DP_MAC_DIV20_CLK_SEL BIT(1)
|
|
#define DPTXPHY_PMA_LANE_RESET_N BIT(2)
|
|
#define DPTXPHY_PMA_LANE_RESET_N_OV BIT(3)
|
|
#define DPTX_PCLK1_SELECT GENMASK(6, 4)
|
|
#define DPTX_PCLK2_SELECT GENMASK(9, 7)
|
|
#define DPRX_PCLK_SELECT GENMASK(12, 10)
|
|
#define DPTX_PCLK1_ENABLE BIT(13)
|
|
#define DPTX_PCLK2_ENABLE BIT(14)
|
|
#define DPRX_PCLK_ENABLE BIT(15)
|
|
|
|
#define ACIOPHY_DP_PCLK_STAT 0x7044
|
|
#define ACIOPHY_AUSPLL_LOCK BIT(3)
|
|
|
|
#define LN0_AUSPMA_RX_TOP 0x9000
|
|
#define LN0_AUSPMA_RX_EQ 0xA000
|
|
#define LN0_AUSPMA_RX_SHM 0xB000
|
|
#define LN0_AUSPMA_TX_TOP 0xC000
|
|
#define LN0_AUSPMA_TX_SHM 0xD000
|
|
|
|
#define LN1_AUSPMA_RX_TOP 0x10000
|
|
#define LN1_AUSPMA_RX_EQ 0x11000
|
|
#define LN1_AUSPMA_RX_SHM 0x12000
|
|
#define LN1_AUSPMA_TX_TOP 0x13000
|
|
#define LN1_AUSPMA_TX_SHM 0x14000
|
|
|
|
#define LN_AUSPMA_RX_TOP_PMAFSM 0x0010
|
|
#define LN_AUSPMA_RX_TOP_PMAFSM_PCS_OV BIT(0)
|
|
#define LN_AUSPMA_RX_TOP_PMAFSM_PCS_REQ BIT(9)
|
|
|
|
#define LN_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE 0x00F0
|
|
#define LN_RX_TXMODE BIT(0)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 0x00
|
|
#define LN_TX_CLK_EN BIT(20)
|
|
#define LN_TX_CLK_EN_OV BIT(21)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 0x04
|
|
#define LN_RX_DIV20_RESET_N_OV BIT(29)
|
|
#define LN_RX_DIV20_RESET_N BIT(30)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 0x08
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 0x0C
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 0x10
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 0x14
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 0x18
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 0x1C
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 0x20
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 0x24
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 0x28
|
|
#define LN_DTVREG_ADJUST GENMASK(31, 27)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 0x2C
|
|
#define LN_DTVREG_BIG_EN BIT(23)
|
|
#define LN_DTVREG_BIG_EN_OV BIT(24)
|
|
#define LN_DTVREG_SML_EN BIT(25)
|
|
#define LN_DTVREG_SML_EN_OV BIT(26)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 0x30
|
|
#define LN_TX_BYTECLK_RESET_SYNC_CLR BIT(22)
|
|
#define LN_TX_BYTECLK_RESET_SYNC_CLR_OV BIT(23)
|
|
#define LN_TX_BYTECLK_RESET_SYNC_EN BIT(24)
|
|
#define LN_TX_BYTECLK_RESET_SYNC_EN_OV BIT(25)
|
|
#define LN_TX_HRCLK_SEL BIT(28)
|
|
#define LN_TX_HRCLK_SEL_OV BIT(29)
|
|
#define LN_TX_PBIAS_EN BIT(30)
|
|
#define LN_TX_PBIAS_EN_OV BIT(31)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 0x34
|
|
#define LN_TX_PRE_EN BIT(0)
|
|
#define LN_TX_PRE_EN_OV BIT(1)
|
|
#define LN_TX_PST1_EN BIT(2)
|
|
#define LN_TX_PST1_EN_OV BIT(3)
|
|
#define LN_DTVREG_ADJUST_OV BIT(15)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_UNK_CTRL14A 0x38
|
|
#define LN_AUSPMA_RX_SHM_TJ_UNK_CTRL14B 0x3C
|
|
#define LN_AUSPMA_RX_SHM_TJ_UNK_CTRL15A 0x40
|
|
#define LN_AUSPMA_RX_SHM_TJ_UNK_CTRL15B 0x44
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 0x48
|
|
#define LN_RXTERM_EN BIT(21)
|
|
#define LN_RXTERM_EN_OV BIT(22)
|
|
#define LN_RXTERM_PULLUP_LEAK_EN BIT(23)
|
|
#define LN_RXTERM_PULLUP_LEAK_EN_OV BIT(24)
|
|
#define LN_TX_CAL_CODE GENMASK(29, 25)
|
|
#define LN_TX_CAL_CODE_OV BIT(30)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 0x4C
|
|
#define LN_TX_MARGIN GENMASK(19, 15)
|
|
#define LN_TX_MARGIN_OV BIT(20)
|
|
#define LN_TX_MARGIN_LSB BIT(21)
|
|
#define LN_TX_MARGIN_LSB_OV BIT(22)
|
|
#define LN_TX_MARGIN_P1 GENMASK(26, 23)
|
|
#define LN_TX_MARGIN_P1_OV BIT(27)
|
|
#define LN_TX_MARGIN_P1_LSB GENMASK(29, 28)
|
|
#define LN_TX_MARGIN_P1_LSB_OV BIT(30)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 0x50
|
|
#define LN_TX_P1_CODE GENMASK(3, 0)
|
|
#define LN_TX_P1_CODE_OV BIT(4)
|
|
#define LN_TX_P1_LSB_CODE GENMASK(6, 5)
|
|
#define LN_TX_P1_LSB_CODE_OV BIT(7)
|
|
#define LN_TX_MARGIN_PRE GENMASK(10, 8)
|
|
#define LN_TX_MARGIN_PRE_OV BIT(11)
|
|
#define LN_TX_MARGIN_PRE_LSB GENMASK(13, 12)
|
|
#define LN_TX_MARGIN_PRE_LSB_OV BIT(14)
|
|
#define LN_TX_PRE_LSB_CODE GENMASK(16, 15)
|
|
#define LN_TX_PRE_LSB_CODE_OV BIT(17)
|
|
#define LN_TX_PRE_CODE GENMASK(21, 18)
|
|
#define LN_TX_PRE_CODE_OV BIT(22)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 0x54
|
|
#define LN_TX_TEST_EN BIT(21)
|
|
#define LN_TX_TEST_EN_OV BIT(22)
|
|
#define LN_TX_EN BIT(23)
|
|
#define LN_TX_EN_OV BIT(24)
|
|
#define LN_TX_CLK_DLY_CTRL_TAPGEN GENMASK(27, 25)
|
|
#define LN_TX_CLK_DIV2_EN BIT(28)
|
|
#define LN_TX_CLK_DIV2_EN_OV BIT(29)
|
|
#define LN_TX_CLK_DIV2_RST BIT(30)
|
|
#define LN_TX_CLK_DIV2_RST_OV BIT(31)
|
|
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 0x58
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 0x5C
|
|
#define LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 0x60
|
|
#define LN_VREF_ADJUST_GRAY GENMASK(11, 7)
|
|
#define LN_VREF_ADJUST_GRAY_OV BIT(12)
|
|
#define LN_VREF_BIAS_SEL GENMASK(14, 13)
|
|
#define LN_VREF_BIAS_SEL_OV BIT(15)
|
|
#define LN_VREF_BOOST_EN BIT(16)
|
|
#define LN_VREF_BOOST_EN_OV BIT(17)
|
|
#define LN_VREF_EN BIT(18)
|
|
#define LN_VREF_EN_OV BIT(19)
|
|
#define LN_VREF_LPBKIN_DATA GENMASK(29, 28)
|
|
#define LN_VREF_TEST_RXLPBKDT_EN BIT(30)
|
|
#define LN_VREF_TEST_RXLPBKDT_EN_OV BIT(31)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 0x00
|
|
#define LN_BYTECLK_RESET_SYNC_EN_OV BIT(2)
|
|
#define LN_BYTECLK_RESET_SYNC_EN BIT(3)
|
|
#define LN_BYTECLK_RESET_SYNC_CLR_OV BIT(4)
|
|
#define LN_BYTECLK_RESET_SYNC_CLR BIT(5)
|
|
#define LN_BYTECLK_RESET_SYNC_SEL_OV BIT(6)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 0x04
|
|
#define LN_TXA_DIV2_EN_OV BIT(8)
|
|
#define LN_TXA_DIV2_EN BIT(9)
|
|
#define LN_TXA_DIV2_RESET_OV BIT(10)
|
|
#define LN_TXA_DIV2_RESET BIT(11)
|
|
#define LN_TXA_CLK_EN_OV BIT(22)
|
|
#define LN_TXA_CLK_EN BIT(23)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_IMP_REG0 0x08
|
|
#define LN_TXA_CAL_CTRL_OV BIT(0)
|
|
#define LN_TXA_CAL_CTRL GENMASK(18, 1)
|
|
#define LN_TXA_CAL_CTRL_BASE_OV BIT(19)
|
|
#define LN_TXA_CAL_CTRL_BASE GENMASK(23, 20)
|
|
#define LN_TXA_HIZ_OV BIT(29)
|
|
#define LN_TXA_HIZ BIT(30)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_IMP_REG1 0x0C
|
|
#define LN_AUSPMA_TX_SHM_TXA_IMP_REG2 0x10
|
|
#define LN_TXA_MARGIN_OV BIT(0)
|
|
#define LN_TXA_MARGIN GENMASK(18, 1)
|
|
#define LN_TXA_MARGIN_2R_OV BIT(19)
|
|
#define LN_TXA_MARGIN_2R BIT(20)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_IMP_REG3 0x14
|
|
#define LN_TXA_MARGIN_POST_OV BIT(0)
|
|
#define LN_TXA_MARGIN_POST GENMASK(10, 1)
|
|
#define LN_TXA_MARGIN_POST_2R_OV BIT(11)
|
|
#define LN_TXA_MARGIN_POST_2R BIT(12)
|
|
#define LN_TXA_MARGIN_POST_4R_OV BIT(13)
|
|
#define LN_TXA_MARGIN_POST_4R BIT(14)
|
|
#define LN_TXA_MARGIN_PRE_OV BIT(15)
|
|
#define LN_TXA_MARGIN_PRE GENMASK(21, 16)
|
|
#define LN_TXA_MARGIN_PRE_2R_OV BIT(22)
|
|
#define LN_TXA_MARGIN_PRE_2R BIT(23)
|
|
#define LN_TXA_MARGIN_PRE_4R_OV BIT(24)
|
|
#define LN_TXA_MARGIN_PRE_4R BIT(25)
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_UNK_REG0 0x18
|
|
#define LN_AUSPMA_TX_SHM_TXA_UNK_REG1 0x1C
|
|
#define LN_AUSPMA_TX_SHM_TXA_UNK_REG2 0x20
|
|
|
|
#define LN_AUSPMA_TX_SHM_TXA_LDOCLK 0x24
|
|
#define LN_LDOCLK_BYPASS_SML_OV BIT(8)
|
|
#define LN_LDOCLK_BYPASS_SML BIT(9)
|
|
#define LN_LDOCLK_BYPASS_BIG_OV BIT(10)
|
|
#define LN_LDOCLK_BYPASS_BIG BIT(11)
|
|
#define LN_LDOCLK_EN_SML_OV BIT(12)
|
|
#define LN_LDOCLK_EN_SML BIT(13)
|
|
#define LN_LDOCLK_EN_BIG_OV BIT(14)
|
|
#define LN_LDOCLK_EN_BIG BIT(15)
|
|
|
|
/* LPDPTX registers */
|
|
#define LPDPTX_AUX_CFG_BLK_AUX_CTRL 0x0000
|
|
#define LPDPTX_BLK_AUX_CTRL_PWRDN BIT(4)
|
|
#define LPDPTX_BLK_AUX_RXOFFSET GENMASK(25, 22)
|
|
|
|
#define LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL 0x0008
|
|
|
|
#define LPDPTX_AUX_CFG_BLK_AUX_MARGIN 0x000c
|
|
#define LPDPTX_MARGIN_RCAL_RXOFFSET_EN BIT(5)
|
|
#define LPDPTX_AUX_MARGIN_RCAL_TXSWING GENMASK(10, 6)
|
|
|
|
#define LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0 0x0204
|
|
#define LPDPTX_CFG_PMA_AUX_SEL_LF_DATA BIT(15)
|
|
|
|
#define LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1 0x0208
|
|
#define LPDPTX_CFG_PMA_PHYS_ADJ GENMASK(22, 20)
|
|
#define LPDPTX_CFG_PMA_PHYS_ADJ_OV BIT(19)
|
|
|
|
#define LPDPTX_AUX_CONTROL 0x4000
|
|
#define LPDPTX_AUX_PWN_DOWN 0x10
|
|
#define LPDPTX_AUX_CLAMP_EN 0x04
|
|
#define LPDPTX_SLEEP_B_BIG_IN 0x02
|
|
#define LPDPTX_SLEEP_B_SML_IN 0x01
|
|
#define LPDPTX_TXTERM_CODEMSB 0x400
|
|
#define LPDPTX_TXTERM_CODE GENMASK(9, 5)
|
|
|
|
/* pipehandler registers */
|
|
#define PIPEHANDLER_OVERRIDE 0x00
|
|
#define PIPEHANDLER_OVERRIDE_RXVALID BIT(0)
|
|
#define PIPEHANDLER_OVERRIDE_RXDETECT BIT(2)
|
|
|
|
#define PIPEHANDLER_OVERRIDE_VALUES 0x04
|
|
#define PIPEHANDLER_OVERRIDE_VAL_RXDETECT0 BIT(1)
|
|
#define PIPEHANDLER_OVERRIDE_VAL_RXDETECT1 BIT(2)
|
|
#define PIPEHANDLER_OVERRIDE_VAL_PHY_STATUS BIT(4)
|
|
|
|
#define PIPEHANDLER_MUX_CTRL 0x0c
|
|
#define PIPEHANDLER_MUX_CTRL_CLK GENMASK(5, 3)
|
|
#define PIPEHANDLER_MUX_CTRL_DATA GENMASK(2, 0)
|
|
#define PIPEHANDLER_MUX_CTRL_CLK_OFF 0
|
|
#define PIPEHANDLER_MUX_CTRL_CLK_USB3 1
|
|
#define PIPEHANDLER_MUX_CTRL_CLK_USB4 2
|
|
#define PIPEHANDLER_MUX_CTRL_CLK_DUMMY 4
|
|
|
|
#define PIPEHANDLER_MUX_CTRL_DATA_USB3 0
|
|
#define PIPEHANDLER_MUX_CTRL_DATA_USB4 1
|
|
#define PIPEHANDLER_MUX_CTRL_DATA_DUMMY 2
|
|
|
|
#define PIPEHANDLER_LOCK_REQ 0x10
|
|
#define PIPEHANDLER_LOCK_ACK 0x14
|
|
#define PIPEHANDLER_LOCK_EN BIT(0)
|
|
|
|
#define PIPEHANDLER_AON_GEN 0x1C
|
|
#define PIPEHANDLER_AON_GEN_DWC3_FORCE_CLAMP_EN BIT(4)
|
|
#define PIPEHANDLER_AON_GEN_DWC3_RESET_N BIT(0)
|
|
|
|
#define PIPEHANDLER_NONSELECTED_OVERRIDE 0x20
|
|
#define PIPEHANDLER_NATIVE_RESET BIT(12)
|
|
#define PIPEHANDLER_DUMMY_PHY_EN BIT(15)
|
|
#define PIPEHANDLER_NATIVE_POWER_DOWN GENMASK(3, 0)
|
|
|
|
#define PIPEHANDLER_LOCK_ACK_TIMEOUT_US 1000
|
|
|
|
/* USB2 PHY regs */
|
|
#define USB2PHY_USBCTL 0x00
|
|
#define USB2PHY_USBCTL_RUN 2
|
|
#define USB2PHY_USBCTL_ISOLATION 4
|
|
|
|
#define USB2PHY_CTL 0x04
|
|
#define USB2PHY_CTL_RESET BIT(0)
|
|
#define USB2PHY_CTL_PORT_RESET BIT(1)
|
|
#define USB2PHY_CTL_APB_RESET_N BIT(2)
|
|
#define USB2PHY_CTL_SIDDQ BIT(3)
|
|
|
|
#define USB2PHY_SIG 0x08
|
|
#define USB2PHY_SIG_VBUSDET_FORCE_VAL BIT(0)
|
|
#define USB2PHY_SIG_VBUSDET_FORCE_EN BIT(1)
|
|
#define USB2PHY_SIG_VBUSVLDEXT_FORCE_VAL BIT(2)
|
|
#define USB2PHY_SIG_VBUSVLDEXT_FORCE_EN BIT(3)
|
|
#define USB2PHY_SIG_HOST (7 << 12)
|
|
|
|
#define USB2PHY_MISCTUNE 0x1c
|
|
#define USB2PHY_MISCTUNE_APBCLK_GATE_OFF BIT(29)
|
|
#define USB2PHY_MISCTUNE_REFCLK_GATE_OFF BIT(30)
|
|
|
|
enum atcphy_dp_link_rate {
|
|
ATCPHY_DP_LINK_RATE_RBR,
|
|
ATCPHY_DP_LINK_RATE_HBR,
|
|
ATCPHY_DP_LINK_RATE_HBR2,
|
|
ATCPHY_DP_LINK_RATE_HBR3,
|
|
};
|
|
|
|
/**
|
|
* enum atcphy_pipehandler_state - States of the PIPE mux interface ("pipehandler")
|
|
* @ATCPHY_PIPEHANDLER_STATE_DUMMY: "Dummy PHY" (disables USB3, USB2 only)
|
|
* @ATCPHY_PIPEHANDLER_STATE_USB3: USB3 directly connected to the Type-C port
|
|
* @ATCPHY_PIPEHANDLER_STATE_USB4: USB3 tunneled via USB4/Thunderbolt
|
|
*
|
|
* DWC3's USB3 PIPE interface is connected to a multiplexer inside this PHY
|
|
* which can switch between a dummy state (which effectively disables any USB3
|
|
* support and falls back to USB2 only operation via the separate ULPI interface),
|
|
* a USB3 state (for regular USB3 or USB3+DisplayPort operation) and a USB4 state
|
|
* (for USB3 tunneled via USB4/Thunderbolt).
|
|
*/
|
|
enum atcphy_pipehandler_state {
|
|
ATCPHY_PIPEHANDLER_STATE_DUMMY,
|
|
ATCPHY_PIPEHANDLER_STATE_USB3,
|
|
ATCPHY_PIPEHANDLER_STATE_USB4,
|
|
};
|
|
|
|
/**
|
|
* enum atcphy_mode - Operating modes of the PHY
|
|
* @APPLE_ATCPHY_MODE_OFF: all PHYs powered off
|
|
* @APPLE_ATCPHY_MODE_USB2: Nothing on the four SS lanes (i.e. USB2 only on D-/+)
|
|
* @APPLE_ATCPHY_MODE_USB3: USB3 on two lanes, nothing on the other two
|
|
* @APPLE_ATCPHY_MODE_USB3_DP: USB3 on two lanes and DisplayPort on the other two
|
|
* @APPLE_ATCPHY_MODE_TBT: Thunderbolt on all lanes
|
|
* @APPLE_ATCPHY_MODE_USB4: USB4 on all lanes
|
|
* @APPLE_ATCPHY_MODE_DP: DisplayPort on all lanes
|
|
*/
|
|
enum atcphy_mode {
|
|
APPLE_ATCPHY_MODE_OFF,
|
|
APPLE_ATCPHY_MODE_USB2,
|
|
APPLE_ATCPHY_MODE_USB3,
|
|
APPLE_ATCPHY_MODE_USB3_DP,
|
|
APPLE_ATCPHY_MODE_TBT,
|
|
APPLE_ATCPHY_MODE_USB4,
|
|
APPLE_ATCPHY_MODE_DP,
|
|
};
|
|
|
|
enum atcphy_lane {
|
|
APPLE_ATCPHY_LANE_0,
|
|
APPLE_ATCPHY_LANE_1,
|
|
};
|
|
|
|
/* Link rate configuration, field names are taken from XNU debug output or register names */
|
|
struct atcphy_dp_link_rate_configuration {
|
|
u16 freqinit_count_target;
|
|
u16 fbdivn_frac_den;
|
|
u16 fbdivn_frac_num;
|
|
u16 pclk_div_sel;
|
|
u8 lfclk_ctrl;
|
|
u8 vclk_op_divn;
|
|
bool plla_clkout_vreg_bypass;
|
|
bool txa_ldoclk_bypass;
|
|
bool txa_div2_en;
|
|
};
|
|
|
|
/* Crossbar and lane configuration */
|
|
struct atcphy_mode_configuration {
|
|
u32 crossbar;
|
|
u32 crossbar_dp_single_pma;
|
|
bool crossbar_dp_both_pma;
|
|
enum atcphy_lane_mode lane_mode[2];
|
|
bool dp_lane[2];
|
|
bool set_swap;
|
|
};
|
|
|
|
/**
|
|
* struct apple_atcphy - Apple Type-C PHY device struct
|
|
* @np: Device node pointer
|
|
* @dev: Device pointer
|
|
* @tunables: Firmware-provided tunable parameters
|
|
* @tunables.axi2af: AXI to AF interface tunables
|
|
* @tunables.common: Common tunables for all lanes
|
|
* @tunables.lane_usb3: USB3 lane-specific tunables
|
|
* @tunables.lane_dp: DisplayPort lane-specific tunables
|
|
* @tunables.lane_usb4: USB4 lane-specific tunables
|
|
* @mode: Current PHY operating mode
|
|
* @swap_lanes: True if lanes must be swapped due to cable orientation
|
|
* @dp_link_rate: DisplayPort link rate
|
|
* @pipehandler_up: True if the PIPE mux ("pipehandler") is set to USB3 or USB4 mode
|
|
* @regs: Memory-mapped registers
|
|
* @regs.core: Core registers
|
|
* @regs.axi2af: AXI to Apple Fabric interface registers
|
|
* @regs.usb2phy: USB2 PHY registers
|
|
* @regs.pipehandler: USB3 PIPE interface ("pipehandler") registers
|
|
* @regs.lpdptx: DisplayPort registers
|
|
* @res: Resources for memory-mapped registers, used to verify that tunables aren't out of bounds
|
|
* @res.core: Core register resource
|
|
* @res.axi2af: AXI to Apple Fabric interface resource
|
|
* @phys: PHY instances
|
|
* @phys.usb2: USB2 PHY instance
|
|
* @phys.usb3: USB3 PHY instance
|
|
* @phys.dp: DisplayPort PHY instance
|
|
* @phy_provider: PHY provider instance
|
|
* @rcdev: Reset controller device
|
|
* @sw: Type-C switch instance
|
|
* @mux: Type-C mux instance
|
|
* @lock: Mutex for synchronizing register access across PHY, Type-C switch/mux and reset controller
|
|
*/
|
|
struct apple_atcphy {
|
|
struct device_node *np;
|
|
struct device *dev;
|
|
|
|
struct {
|
|
struct apple_tunable *axi2af;
|
|
struct apple_tunable *common[2];
|
|
struct apple_tunable *lane_usb3[2];
|
|
struct apple_tunable *lane_dp[2];
|
|
struct apple_tunable *lane_usb4[2];
|
|
} tunables;
|
|
|
|
enum atcphy_mode mode;
|
|
bool swap_lanes;
|
|
int dp_link_rate;
|
|
bool pipehandler_up;
|
|
|
|
struct {
|
|
void __iomem *core;
|
|
void __iomem *axi2af;
|
|
void __iomem *usb2phy;
|
|
void __iomem *pipehandler;
|
|
void __iomem *lpdptx;
|
|
} regs;
|
|
|
|
struct {
|
|
struct resource *core;
|
|
struct resource *axi2af;
|
|
} res;
|
|
|
|
struct {
|
|
struct phy *usb2;
|
|
struct phy *usb3;
|
|
struct phy *dp;
|
|
} phys;
|
|
struct phy_provider *phy_provider;
|
|
|
|
struct reset_controller_dev rcdev;
|
|
|
|
struct typec_switch *sw;
|
|
struct typec_mux *mux;
|
|
|
|
struct mutex lock;
|
|
};
|
|
|
|
static const struct {
|
|
const struct atcphy_mode_configuration normal;
|
|
const struct atcphy_mode_configuration swapped;
|
|
bool enable_dp_aux;
|
|
enum atcphy_pipehandler_state pipehandler_state;
|
|
} atcphy_modes[] = {
|
|
[APPLE_ATCPHY_MODE_OFF] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_OFF, ACIOPHY_LANE_MODE_OFF},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_OFF, ACIOPHY_LANE_MODE_OFF},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false, /* doesn't matter since the SS lanes are off */
|
|
},
|
|
.enable_dp_aux = false,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_DUMMY,
|
|
},
|
|
[APPLE_ATCPHY_MODE_USB2] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_OFF, ACIOPHY_LANE_MODE_OFF},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_OFF, ACIOPHY_LANE_MODE_OFF},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false, /* doesn't matter since the SS lanes are off */
|
|
},
|
|
.enable_dp_aux = false,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_DUMMY,
|
|
},
|
|
[APPLE_ATCPHY_MODE_USB3] = {
|
|
/*
|
|
* Setting up the lanes as DP/USB3 is intentional here, USB3/USB3 does not work
|
|
* and isn't required since this PHY does not support 20GBps mode anyway.
|
|
* The only difference to APPLE_ATCPHY_MODE_USB3_DP is that DP Aux is not enabled.
|
|
*/
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB3, ACIOPHY_LANE_MODE_DP},
|
|
.dp_lane = {false, true},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_DP, ACIOPHY_LANE_MODE_USB3},
|
|
.dp_lane = {true, false},
|
|
.set_swap = true,
|
|
},
|
|
.enable_dp_aux = false,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_USB3,
|
|
},
|
|
[APPLE_ATCPHY_MODE_USB3_DP] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB3, ACIOPHY_LANE_MODE_DP},
|
|
.dp_lane = {false, true},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB3_DP_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_DP, ACIOPHY_LANE_MODE_USB3},
|
|
.dp_lane = {true, false},
|
|
.set_swap = true,
|
|
},
|
|
.enable_dp_aux = true,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_USB3,
|
|
},
|
|
[APPLE_ATCPHY_MODE_TBT] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB4,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB4, ACIOPHY_LANE_MODE_USB4},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB4_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB4, ACIOPHY_LANE_MODE_USB4},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false, /* intentionally false */
|
|
},
|
|
.enable_dp_aux = false,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_DUMMY,
|
|
},
|
|
[APPLE_ATCPHY_MODE_USB4] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB4,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB4, ACIOPHY_LANE_MODE_USB4},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_USB4_SWAPPED,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_NONE,
|
|
.crossbar_dp_both_pma = false,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_USB4, ACIOPHY_LANE_MODE_USB4},
|
|
.dp_lane = {false, false},
|
|
.set_swap = false, /* intentionally false */
|
|
},
|
|
.enable_dp_aux = false,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_USB4,
|
|
},
|
|
[APPLE_ATCPHY_MODE_DP] = {
|
|
.normal = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_DP,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK100,
|
|
.crossbar_dp_both_pma = true,
|
|
.lane_mode = {ACIOPHY_LANE_MODE_DP, ACIOPHY_LANE_MODE_DP},
|
|
.dp_lane = {true, true},
|
|
.set_swap = false,
|
|
},
|
|
.swapped = {
|
|
.crossbar = ACIOPHY_CROSSBAR_PROTOCOL_DP,
|
|
.crossbar_dp_single_pma = ACIOPHY_CROSSBAR_DP_SINGLE_PMA_UNK008,
|
|
.crossbar_dp_both_pma = false, /* intentionally false */
|
|
.lane_mode = {ACIOPHY_LANE_MODE_DP, ACIOPHY_LANE_MODE_DP},
|
|
.dp_lane = {true, true},
|
|
.set_swap = false, /* intentionally false */
|
|
},
|
|
.enable_dp_aux = true,
|
|
.pipehandler_state = ATCPHY_PIPEHANDLER_STATE_DUMMY,
|
|
},
|
|
};
|
|
|
|
static const struct atcphy_dp_link_rate_configuration dp_lr_config[] = {
|
|
[ATCPHY_DP_LINK_RATE_RBR] = {
|
|
.freqinit_count_target = 0x21c,
|
|
.fbdivn_frac_den = 0x0,
|
|
.fbdivn_frac_num = 0x0,
|
|
.pclk_div_sel = 0x13,
|
|
.lfclk_ctrl = 0x5,
|
|
.vclk_op_divn = 0x2,
|
|
.plla_clkout_vreg_bypass = true,
|
|
.txa_ldoclk_bypass = true,
|
|
.txa_div2_en = true,
|
|
},
|
|
[ATCPHY_DP_LINK_RATE_HBR] = {
|
|
.freqinit_count_target = 0x1c2,
|
|
.fbdivn_frac_den = 0x3ffe,
|
|
.fbdivn_frac_num = 0x1fff,
|
|
.pclk_div_sel = 0x9,
|
|
.lfclk_ctrl = 0x5,
|
|
.vclk_op_divn = 0x2,
|
|
.plla_clkout_vreg_bypass = true,
|
|
.txa_ldoclk_bypass = true,
|
|
.txa_div2_en = false,
|
|
},
|
|
[ATCPHY_DP_LINK_RATE_HBR2] = {
|
|
.freqinit_count_target = 0x1c2,
|
|
.fbdivn_frac_den = 0x3ffe,
|
|
.fbdivn_frac_num = 0x1fff,
|
|
.pclk_div_sel = 0x4,
|
|
.lfclk_ctrl = 0x5,
|
|
.vclk_op_divn = 0x0,
|
|
.plla_clkout_vreg_bypass = true,
|
|
.txa_ldoclk_bypass = true,
|
|
.txa_div2_en = false,
|
|
},
|
|
[ATCPHY_DP_LINK_RATE_HBR3] = {
|
|
.freqinit_count_target = 0x2a3,
|
|
.fbdivn_frac_den = 0x3ffc,
|
|
.fbdivn_frac_num = 0x2ffd,
|
|
.pclk_div_sel = 0x4,
|
|
.lfclk_ctrl = 0x6,
|
|
.vclk_op_divn = 0x0,
|
|
.plla_clkout_vreg_bypass = false,
|
|
.txa_ldoclk_bypass = false,
|
|
.txa_div2_en = false,
|
|
},
|
|
};
|
|
|
|
static inline void mask32(void __iomem *reg, u32 mask, u32 set)
|
|
{
|
|
u32 value = readl(reg);
|
|
|
|
value &= ~mask;
|
|
value |= set;
|
|
writel(value, reg);
|
|
}
|
|
|
|
static inline void core_mask32(struct apple_atcphy *atcphy, u32 reg, u32 mask, u32 set)
|
|
{
|
|
mask32(atcphy->regs.core + reg, mask, set);
|
|
}
|
|
|
|
static inline void set32(void __iomem *reg, u32 set)
|
|
{
|
|
mask32(reg, 0, set);
|
|
}
|
|
|
|
static inline void core_set32(struct apple_atcphy *atcphy, u32 reg, u32 set)
|
|
{
|
|
core_mask32(atcphy, reg, 0, set);
|
|
}
|
|
|
|
static inline void clear32(void __iomem *reg, u32 clear)
|
|
{
|
|
mask32(reg, clear, 0);
|
|
}
|
|
|
|
static inline void core_clear32(struct apple_atcphy *atcphy, u32 reg, u32 clear)
|
|
{
|
|
core_mask32(atcphy, reg, clear, 0);
|
|
}
|
|
|
|
static const struct atcphy_mode_configuration *atcphy_get_mode_config(struct apple_atcphy *atcphy,
|
|
enum atcphy_mode mode)
|
|
{
|
|
if (atcphy->swap_lanes)
|
|
return &atcphy_modes[mode].swapped;
|
|
else
|
|
return &atcphy_modes[mode].normal;
|
|
}
|
|
|
|
static void atcphy_apply_tunables(struct apple_atcphy *atcphy, enum atcphy_mode mode)
|
|
{
|
|
const int lane0 = atcphy->swap_lanes ? 1 : 0;
|
|
const int lane1 = atcphy->swap_lanes ? 0 : 1;
|
|
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.common[0]);
|
|
apple_tunable_apply(atcphy->regs.axi2af, atcphy->tunables.axi2af);
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.common[1]);
|
|
|
|
switch (mode) {
|
|
/*
|
|
* USB 3.2 Gen 2x2 / SuperSpeed 20Gbps is not supported by this hardware and applying USB3
|
|
* tunables to both lanes does not result in a working PHY configuration. Thus, both
|
|
* USB3-only and USB3/DP get the same tunable setup here.
|
|
*/
|
|
case APPLE_ATCPHY_MODE_USB3:
|
|
case APPLE_ATCPHY_MODE_USB3_DP:
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_usb3[lane0]);
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_dp[lane1]);
|
|
break;
|
|
|
|
case APPLE_ATCPHY_MODE_DP:
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_dp[lane0]);
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_dp[lane1]);
|
|
break;
|
|
|
|
/*
|
|
* Even though the various Thunderbolt versions and USB4 are different protocols they need
|
|
* the same tunables. The actual protocol-specific setup happens inside the Thunderbolt/USB4
|
|
* native host interface.
|
|
*/
|
|
case APPLE_ATCPHY_MODE_TBT:
|
|
case APPLE_ATCPHY_MODE_USB4:
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_usb4[lane0]);
|
|
apple_tunable_apply(atcphy->regs.core, atcphy->tunables.lane_usb4[lane1]);
|
|
break;
|
|
|
|
case APPLE_ATCPHY_MODE_OFF:
|
|
case APPLE_ATCPHY_MODE_USB2:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int atcphy_pipehandler_lock(struct apple_atcphy *atcphy)
|
|
{
|
|
int ret;
|
|
u32 reg;
|
|
|
|
if (readl(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ) & PIPEHANDLER_LOCK_EN) {
|
|
dev_warn(atcphy->dev, "Pipehandler already locked\n");
|
|
return 0;
|
|
}
|
|
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, PIPEHANDLER_LOCK_EN);
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_ACK, reg,
|
|
reg & PIPEHANDLER_LOCK_EN, 10, PIPEHANDLER_LOCK_ACK_TIMEOUT_US);
|
|
if (ret) {
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, 1);
|
|
dev_warn(atcphy->dev, "Pipehandler lock not acked.\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int atcphy_pipehandler_unlock(struct apple_atcphy *atcphy)
|
|
{
|
|
int ret;
|
|
u32 reg;
|
|
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, PIPEHANDLER_LOCK_EN);
|
|
ret = readl_poll_timeout(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_ACK, reg,
|
|
!(reg & PIPEHANDLER_LOCK_EN), 10, PIPEHANDLER_LOCK_ACK_TIMEOUT_US);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Pipehandler lock release not acked.\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int atcphy_pipehandler_check(struct apple_atcphy *atcphy)
|
|
{
|
|
int ret;
|
|
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
if (readl(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_ACK) & PIPEHANDLER_LOCK_EN) {
|
|
dev_warn(atcphy->dev, "Pipehandler already locked\n");
|
|
|
|
ret = atcphy_pipehandler_unlock(atcphy);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "Failed to unlock pipehandler\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool host)
|
|
{
|
|
int ret;
|
|
u32 reg;
|
|
|
|
ret = atcphy_pipehandler_check(atcphy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Only host mode requires this unknown BIST sequence to work correctly, possibly due to
|
|
* some hardware quirk. Guest mode breaks if we try to apply this sequence.
|
|
*/
|
|
if (host) {
|
|
/* Force disable link detection */
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE_VALUES,
|
|
PIPEHANDLER_OVERRIDE_VAL_RXDETECT0 | PIPEHANDLER_OVERRIDE_VAL_RXDETECT1);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE,
|
|
PIPEHANDLER_OVERRIDE_RXVALID);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE,
|
|
PIPEHANDLER_OVERRIDE_RXDETECT);
|
|
|
|
ret = atcphy_pipehandler_lock(atcphy);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "Failed to lock pipehandler");
|
|
return ret;
|
|
}
|
|
|
|
/* BIST dance */
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_PHY_CFG0,
|
|
ACIOPHY_TOP_BIST_PHY_CFG0_LN0_RESET_N);
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_OV_CFG, ACIOPHY_TOP_BIST_OV_CFG_LN0_RESET_N_OV);
|
|
ret = readl_poll_timeout(atcphy->regs.core + ACIOPHY_TOP_PHY_STAT, reg,
|
|
!(reg & ACIOPHY_TOP_PHY_STAT_LN0_UNK23), 10, 10000);
|
|
if (ret)
|
|
dev_warn(atcphy->dev,
|
|
"Timed out waiting for ACIOPHY_TOP_PHY_STAT_LN0_UNK23\n");
|
|
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_READ_CTRL,
|
|
ACIOPHY_TOP_BIST_READ_CTRL_LN0_PHY_STATUS_RE);
|
|
core_clear32(atcphy, ACIOPHY_TOP_BIST_READ_CTRL,
|
|
ACIOPHY_TOP_BIST_READ_CTRL_LN0_PHY_STATUS_RE);
|
|
|
|
core_mask32(atcphy, ACIOPHY_TOP_BIST_PHY_CFG1,
|
|
ACIOPHY_TOP_BIST_PHY_CFG1_LN0_PWR_DOWN,
|
|
FIELD_PREP(ACIOPHY_TOP_BIST_PHY_CFG1_LN0_PWR_DOWN, 3));
|
|
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_OV_CFG,
|
|
ACIOPHY_TOP_BIST_OV_CFG_LN0_PWR_DOWN_OV);
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_CIOPHY_CFG1,
|
|
ACIOPHY_TOP_BIST_CIOPHY_CFG1_CLK_EN);
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_CIOPHY_CFG1,
|
|
ACIOPHY_TOP_BIST_CIOPHY_CFG1_BIST_EN);
|
|
writel(0, atcphy->regs.core + ACIOPHY_TOP_BIST_CIOPHY_CFG1);
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.core + ACIOPHY_TOP_PHY_STAT, reg,
|
|
(reg & ACIOPHY_TOP_PHY_STAT_LN0_UNK0), 10, 10000);
|
|
if (ret)
|
|
dev_warn(atcphy->dev,
|
|
"timed out waiting for ACIOPHY_TOP_PHY_STAT_LN0_UNK0\n");
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.core + ACIOPHY_TOP_PHY_STAT, reg,
|
|
!(reg & ACIOPHY_TOP_PHY_STAT_LN0_UNK23), 10, 10000);
|
|
if (ret)
|
|
dev_warn(atcphy->dev,
|
|
"timed out waiting for ACIOPHY_TOP_PHY_STAT_LN0_UNK23\n");
|
|
|
|
/* Clear reset for non-selected USB3 PHY (?) */
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_NONSELECTED_OVERRIDE,
|
|
PIPEHANDLER_NATIVE_POWER_DOWN, FIELD_PREP(PIPEHANDLER_NATIVE_POWER_DOWN, 3));
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_NONSELECTED_OVERRIDE,
|
|
PIPEHANDLER_NATIVE_RESET);
|
|
|
|
/* More BIST stuff (?) */
|
|
writel(0, atcphy->regs.core + ACIOPHY_TOP_BIST_OV_CFG);
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_CIOPHY_CFG1,
|
|
ACIOPHY_TOP_BIST_CIOPHY_CFG1_CLK_EN);
|
|
core_set32(atcphy, ACIOPHY_TOP_BIST_CIOPHY_CFG1,
|
|
ACIOPHY_TOP_BIST_CIOPHY_CFG1_BIST_EN);
|
|
}
|
|
|
|
/* Configure PIPE mux to USB3 PHY */
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_USB3));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_USB3));
|
|
udelay(10);
|
|
|
|
/* Remove link detection override */
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
|
|
|
|
/* Pipehandler was only locked when the BIST sequence was applied for host mode */
|
|
if (host) {
|
|
ret = atcphy_pipehandler_unlock(atcphy);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Failed to unlock pipehandler");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
|
|
{
|
|
int ret;
|
|
|
|
ret = atcphy_pipehandler_check(atcphy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Force disable link detection */
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE_VALUES,
|
|
PIPEHANDLER_OVERRIDE_VAL_RXDETECT0 | PIPEHANDLER_OVERRIDE_VAL_RXDETECT1);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
|
|
|
|
ret = atcphy_pipehandler_lock(atcphy);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Failed to lock pipehandler");
|
|
|
|
/* Switch to dummy PHY */
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
|
|
udelay(10);
|
|
|
|
ret = atcphy_pipehandler_unlock(atcphy);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Failed to unlock pipehandler");
|
|
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_NONSELECTED_OVERRIDE,
|
|
PIPEHANDLER_NATIVE_POWER_DOWN, FIELD_PREP(PIPEHANDLER_NATIVE_POWER_DOWN, 2));
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_NONSELECTED_OVERRIDE,
|
|
PIPEHANDLER_NATIVE_RESET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_configure_pipehandler(struct apple_atcphy *atcphy, bool host)
|
|
{
|
|
int ret;
|
|
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
switch (atcphy_modes[atcphy->mode].pipehandler_state) {
|
|
case ATCPHY_PIPEHANDLER_STATE_USB3:
|
|
ret = atcphy_configure_pipehandler_usb3(atcphy, host);
|
|
atcphy->pipehandler_up = true;
|
|
break;
|
|
case ATCPHY_PIPEHANDLER_STATE_USB4:
|
|
dev_warn(atcphy->dev,
|
|
"ATCPHY_PIPEHANDLER_STATE_USB4 not implemented; falling back to USB2\n");
|
|
ret = atcphy_configure_pipehandler_dummy(atcphy);
|
|
atcphy->pipehandler_up = false;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void atcphy_setup_pipehandler(struct apple_atcphy *atcphy)
|
|
{
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
|
|
udelay(10);
|
|
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
|
|
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
|
|
udelay(10);
|
|
}
|
|
|
|
static void atcphy_configure_lanes(struct apple_atcphy *atcphy, enum atcphy_mode mode)
|
|
{
|
|
const struct atcphy_mode_configuration *mode_cfg = atcphy_get_mode_config(atcphy, mode);
|
|
|
|
core_mask32(atcphy, ACIOPHY_LANE_MODE, ACIOPHY_LANE_MODE_RX0,
|
|
FIELD_PREP(ACIOPHY_LANE_MODE_RX0, mode_cfg->lane_mode[0]));
|
|
core_mask32(atcphy, ACIOPHY_LANE_MODE, ACIOPHY_LANE_MODE_TX0,
|
|
FIELD_PREP(ACIOPHY_LANE_MODE_TX0, mode_cfg->lane_mode[0]));
|
|
core_mask32(atcphy, ACIOPHY_LANE_MODE, ACIOPHY_LANE_MODE_RX1,
|
|
FIELD_PREP(ACIOPHY_LANE_MODE_RX1, mode_cfg->lane_mode[1]));
|
|
core_mask32(atcphy, ACIOPHY_LANE_MODE, ACIOPHY_LANE_MODE_TX1,
|
|
FIELD_PREP(ACIOPHY_LANE_MODE_TX1, mode_cfg->lane_mode[1]));
|
|
core_mask32(atcphy, ACIOPHY_CROSSBAR, ACIOPHY_CROSSBAR_PROTOCOL,
|
|
FIELD_PREP(ACIOPHY_CROSSBAR_PROTOCOL, mode_cfg->crossbar));
|
|
|
|
if (mode_cfg->set_swap)
|
|
core_set32(atcphy, ATCPHY_MISC, ATCPHY_MISC_LANE_SWAP);
|
|
else
|
|
core_clear32(atcphy, ATCPHY_MISC, ATCPHY_MISC_LANE_SWAP);
|
|
|
|
core_mask32(atcphy, ACIOPHY_CROSSBAR, ACIOPHY_CROSSBAR_DP_SINGLE_PMA,
|
|
FIELD_PREP(ACIOPHY_CROSSBAR_DP_SINGLE_PMA, mode_cfg->crossbar_dp_single_pma));
|
|
if (mode_cfg->crossbar_dp_both_pma)
|
|
core_set32(atcphy, ACIOPHY_CROSSBAR, ACIOPHY_CROSSBAR_DP_BOTH_PMA);
|
|
else
|
|
core_clear32(atcphy, ACIOPHY_CROSSBAR, ACIOPHY_CROSSBAR_DP_BOTH_PMA);
|
|
|
|
if (mode_cfg->dp_lane[0]) {
|
|
core_set32(atcphy, LN0_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_OV);
|
|
udelay(10);
|
|
core_clear32(atcphy, LN0_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_REQ);
|
|
} else {
|
|
core_clear32(atcphy, LN0_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_OV);
|
|
udelay(10);
|
|
}
|
|
|
|
if (mode_cfg->dp_lane[1]) {
|
|
core_set32(atcphy, LN1_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_OV);
|
|
udelay(10);
|
|
core_clear32(atcphy, LN1_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_REQ);
|
|
} else {
|
|
core_clear32(atcphy, LN1_AUSPMA_RX_TOP + LN_AUSPMA_RX_TOP_PMAFSM,
|
|
LN_AUSPMA_RX_TOP_PMAFSM_PCS_OV);
|
|
udelay(10);
|
|
}
|
|
}
|
|
|
|
static void atcphy_enable_dp_aux(struct apple_atcphy *atcphy)
|
|
{
|
|
core_set32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTXPHY_PMA_LANE_RESET_N);
|
|
core_set32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTXPHY_PMA_LANE_RESET_N_OV);
|
|
|
|
core_mask32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPRX_PCLK_SELECT,
|
|
FIELD_PREP(DPRX_PCLK_SELECT, 1));
|
|
core_set32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPRX_PCLK_ENABLE);
|
|
|
|
core_mask32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK1_SELECT,
|
|
FIELD_PREP(DPTX_PCLK1_SELECT, 1));
|
|
core_set32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK1_ENABLE);
|
|
|
|
core_mask32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK2_SELECT,
|
|
FIELD_PREP(DPTX_PCLK2_SELECT, 1));
|
|
core_set32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK2_ENABLE);
|
|
|
|
core_set32(atcphy, ACIOPHY_PLL_COMMON_CTRL,
|
|
ACIOPHY_PLL_WAIT_FOR_CMN_READY_BEFORE_RESET_EXIT);
|
|
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_AUX_CLAMP_EN);
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_SLEEP_B_SML_IN);
|
|
udelay(10);
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_SLEEP_B_BIG_IN);
|
|
udelay(10);
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_AUX_CLAMP_EN);
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_AUX_PWN_DOWN);
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_TXTERM_CODEMSB);
|
|
mask32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_TXTERM_CODE,
|
|
FIELD_PREP(LPDPTX_TXTERM_CODE, 0x16));
|
|
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL, 0x1c00);
|
|
mask32(atcphy->regs.lpdptx + LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1, LPDPTX_CFG_PMA_PHYS_ADJ,
|
|
FIELD_PREP(LPDPTX_CFG_PMA_PHYS_ADJ, 5));
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1,
|
|
LPDPTX_CFG_PMA_PHYS_ADJ_OV);
|
|
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_MARGIN,
|
|
LPDPTX_MARGIN_RCAL_RXOFFSET_EN);
|
|
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_CTRL, LPDPTX_BLK_AUX_CTRL_PWRDN);
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0,
|
|
LPDPTX_CFG_PMA_AUX_SEL_LF_DATA);
|
|
mask32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_CTRL, LPDPTX_BLK_AUX_RXOFFSET,
|
|
FIELD_PREP(LPDPTX_BLK_AUX_RXOFFSET, 3));
|
|
|
|
mask32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_MARGIN, LPDPTX_AUX_MARGIN_RCAL_TXSWING,
|
|
FIELD_PREP(LPDPTX_AUX_MARGIN_RCAL_TXSWING, 12));
|
|
|
|
atcphy->dp_link_rate = -1;
|
|
}
|
|
|
|
static void atcphy_disable_dp_aux(struct apple_atcphy *atcphy)
|
|
{
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_AUX_PWN_DOWN);
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CFG_BLK_AUX_CTRL, LPDPTX_BLK_AUX_CTRL_PWRDN);
|
|
set32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_AUX_CLAMP_EN);
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_SLEEP_B_SML_IN);
|
|
udelay(10);
|
|
clear32(atcphy->regs.lpdptx + LPDPTX_AUX_CONTROL, LPDPTX_SLEEP_B_BIG_IN);
|
|
udelay(10);
|
|
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTXPHY_PMA_LANE_RESET_N);
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPRX_PCLK_ENABLE);
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK1_ENABLE);
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DPTX_PCLK2_ENABLE);
|
|
}
|
|
|
|
static int atcphy_dp_configure_lane(struct apple_atcphy *atcphy, enum atcphy_lane lane,
|
|
const struct atcphy_dp_link_rate_configuration *cfg)
|
|
{
|
|
void __iomem *tx_shm, *rx_shm, *rx_top;
|
|
unsigned int tx_cal_code;
|
|
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
switch (lane) {
|
|
case APPLE_ATCPHY_LANE_0:
|
|
tx_shm = atcphy->regs.core + LN0_AUSPMA_TX_SHM;
|
|
rx_shm = atcphy->regs.core + LN0_AUSPMA_RX_SHM;
|
|
rx_top = atcphy->regs.core + LN0_AUSPMA_RX_TOP;
|
|
break;
|
|
case APPLE_ATCPHY_LANE_1:
|
|
tx_shm = atcphy->regs.core + LN1_AUSPMA_TX_SHM;
|
|
rx_shm = atcphy->regs.core + LN1_AUSPMA_RX_SHM;
|
|
rx_top = atcphy->regs.core + LN1_AUSPMA_RX_TOP;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_EN_SML);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_EN_SML_OV);
|
|
udelay(10);
|
|
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_EN_BIG);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_EN_BIG_OV);
|
|
udelay(10);
|
|
|
|
if (cfg->txa_ldoclk_bypass) {
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_SML);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_SML_OV);
|
|
udelay(10);
|
|
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_BIG);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_BIG_OV);
|
|
udelay(10);
|
|
} else {
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_SML);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_SML_OV);
|
|
udelay(10);
|
|
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_BIG);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_LDOCLK, LN_LDOCLK_BYPASS_BIG_OV);
|
|
udelay(10);
|
|
}
|
|
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0, LN_BYTECLK_RESET_SYNC_SEL_OV);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0, LN_BYTECLK_RESET_SYNC_EN);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0, LN_BYTECLK_RESET_SYNC_EN_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0, LN_BYTECLK_RESET_SYNC_CLR);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0, LN_BYTECLK_RESET_SYNC_CLR_OV);
|
|
|
|
if (cfg->txa_div2_en)
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_DIV2_EN);
|
|
else
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_DIV2_EN);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_DIV2_EN_OV);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_CLK_EN);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_CLK_EN_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_DIV2_RESET);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1, LN_TXA_DIV2_RESET_OV);
|
|
|
|
mask32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_CAL_CTRL_BASE,
|
|
FIELD_PREP(LN_TXA_CAL_CTRL_BASE, 0xf));
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_CAL_CTRL_BASE_OV);
|
|
|
|
tx_cal_code = FIELD_GET(AUS_UNK_A20_TX_CAL_CODE, readl(atcphy->regs.core + AUS_UNK_A20));
|
|
mask32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_CAL_CTRL,
|
|
FIELD_PREP(LN_TXA_CAL_CTRL, (1 << tx_cal_code) - 1));
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_CAL_CTRL_OV);
|
|
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG2, LN_TXA_MARGIN);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG2, LN_TXA_MARGIN_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG2, LN_TXA_MARGIN_2R);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG2, LN_TXA_MARGIN_2R_OV);
|
|
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST_2R);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST_2R_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST_4R);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_POST_4R_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE_2R);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE_2R_OV);
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE_4R);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG3, LN_TXA_MARGIN_PRE_4R_OV);
|
|
|
|
clear32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_HIZ);
|
|
set32(tx_shm + LN_AUSPMA_TX_SHM_TXA_IMP_REG0, LN_TXA_HIZ_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1, LN_RX_DIV20_RESET_N);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1, LN_RX_DIV20_RESET_N_OV);
|
|
udelay(10);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1, LN_RX_DIV20_RESET_N);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_BYTECLK_RESET_SYNC_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_BYTECLK_RESET_SYNC_EN_OV);
|
|
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_TX_CAL_CODE,
|
|
FIELD_PREP(LN_TX_CAL_CODE, tx_cal_code));
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_TX_CAL_CODE_OV);
|
|
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DLY_CTRL_TAPGEN,
|
|
FIELD_PREP(LN_TX_CLK_DLY_CTRL_TAPGEN, 3));
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10, LN_DTVREG_ADJUST);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_DTVREG_ADJUST_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_RXTERM_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_RXTERM_EN_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_TEST_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_TEST_EN_OV);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_TEST_RXLPBKDT_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_TEST_RXLPBKDT_EN_OV);
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_LPBKIN_DATA,
|
|
FIELD_PREP(LN_VREF_LPBKIN_DATA, 3));
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BIAS_SEL,
|
|
FIELD_PREP(LN_VREF_BIAS_SEL, 2));
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BIAS_SEL_OV);
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_ADJUST_GRAY,
|
|
FIELD_PREP(LN_VREF_ADJUST_GRAY, 0x18));
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_ADJUST_GRAY_OV);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_EN_OV);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BOOST_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BOOST_EN_OV);
|
|
udelay(10);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BOOST_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22, LN_VREF_BOOST_EN_OV);
|
|
udelay(10);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_TX_PRE_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_TX_PRE_EN_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_TX_PST1_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_TX_PST1_EN_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_PBIAS_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_PBIAS_EN_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_RXTERM_PULLUP_LEAK_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16, LN_RXTERM_PULLUP_LEAK_EN_OV);
|
|
|
|
set32(rx_top + LN_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE, LN_RX_TXMODE);
|
|
|
|
if (cfg->txa_div2_en)
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DIV2_EN);
|
|
else
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DIV2_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DIV2_EN_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DIV2_RST);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_CLK_DIV2_RST_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_HRCLK_SEL);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_HRCLK_SEL_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_LSB);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_LSB_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_P1);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_P1_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_P1_LSB);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17, LN_TX_MARGIN_P1_LSB_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_P1_CODE);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_P1_CODE_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_P1_LSB_CODE);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_P1_LSB_CODE_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_MARGIN_PRE);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_MARGIN_PRE_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_MARGIN_PRE_LSB);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_MARGIN_PRE_LSB_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_PRE_LSB_CODE);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_PRE_LSB_CODE_OV);
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_PRE_CODE);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18, LN_TX_PRE_CODE_OV);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11, LN_DTVREG_SML_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11, LN_DTVREG_SML_EN_OV);
|
|
udelay(10);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11, LN_DTVREG_BIG_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11, LN_DTVREG_BIG_EN_OV);
|
|
udelay(10);
|
|
|
|
mask32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10, LN_DTVREG_ADJUST,
|
|
FIELD_PREP(LN_DTVREG_ADJUST, 0xa));
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13, LN_DTVREG_ADJUST_OV);
|
|
udelay(10);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19, LN_TX_EN_OV);
|
|
udelay(10);
|
|
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0, LN_TX_CLK_EN);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0, LN_TX_CLK_EN_OV);
|
|
|
|
clear32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_BYTECLK_RESET_SYNC_CLR);
|
|
set32(rx_shm + LN_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12, LN_TX_BYTECLK_RESET_SYNC_CLR_OV);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_auspll_apb_command(struct apple_atcphy *atcphy, u32 command)
|
|
{
|
|
int ret;
|
|
u32 reg;
|
|
|
|
reg = readl(atcphy->regs.core + AUSPLL_APB_CMD_OVERRIDE);
|
|
reg &= ~AUSPLL_APB_CMD_OVERRIDE_CMD;
|
|
reg |= FIELD_PREP(AUSPLL_APB_CMD_OVERRIDE_CMD, command);
|
|
reg |= AUSPLL_APB_CMD_OVERRIDE_REQ;
|
|
reg |= AUSPLL_APB_CMD_OVERRIDE_UNK28;
|
|
writel(reg, atcphy->regs.core + AUSPLL_APB_CMD_OVERRIDE);
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.core + AUSPLL_APB_CMD_OVERRIDE, reg,
|
|
(reg & AUSPLL_APB_CMD_OVERRIDE_ACK), 10, 10000);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "AUSPLL APB command was not acked\n");
|
|
|
|
core_clear32(atcphy, AUSPLL_APB_CMD_OVERRIDE, AUSPLL_APB_CMD_OVERRIDE_REQ);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_dp_configure(struct apple_atcphy *atcphy, enum atcphy_dp_link_rate lr)
|
|
{
|
|
const struct atcphy_dp_link_rate_configuration *cfg;
|
|
const struct atcphy_mode_configuration *mode_cfg;
|
|
int ret;
|
|
u32 reg;
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
mode_cfg = atcphy_get_mode_config(atcphy, atcphy->mode);
|
|
cfg = &dp_lr_config[lr];
|
|
|
|
if (atcphy->dp_link_rate == lr)
|
|
return 0;
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.core + ACIOPHY_CMN_SHM_STS_REG0, reg,
|
|
(reg & ACIOPHY_CMN_SHM_STS_REG0_CMD_READY), 10, 10000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "ACIOPHY_CMN_SHM_STS_REG0_CMD_READY not set.\n");
|
|
return ret;
|
|
}
|
|
|
|
core_clear32(atcphy, AUSPLL_FREQ_CFG, AUSPLL_FREQ_REFCLK);
|
|
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_FREQ_COUNT_TARGET,
|
|
FIELD_PREP(AUSPLL_FD_FREQ_COUNT_TARGET, cfg->freqinit_count_target));
|
|
core_clear32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_FBDIVN_HALF);
|
|
core_clear32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_REV_DIVN);
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_KI_MAN, FIELD_PREP(AUSPLL_FD_KI_MAN, 8));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_KI_EXP, FIELD_PREP(AUSPLL_FD_KI_EXP, 3));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_KP_MAN, FIELD_PREP(AUSPLL_FD_KP_MAN, 8));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_KP_EXP, FIELD_PREP(AUSPLL_FD_KP_EXP, 7));
|
|
core_clear32(atcphy, AUSPLL_FREQ_DESC_A, AUSPLL_FD_KPKI_SCALE_HBW);
|
|
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_B, AUSPLL_FD_FBDIVN_FRAC_DEN,
|
|
FIELD_PREP(AUSPLL_FD_FBDIVN_FRAC_DEN, cfg->fbdivn_frac_den));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_B, AUSPLL_FD_FBDIVN_FRAC_NUM,
|
|
FIELD_PREP(AUSPLL_FD_FBDIVN_FRAC_NUM, cfg->fbdivn_frac_num));
|
|
|
|
core_clear32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_SDM_SSC_STEP);
|
|
core_clear32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_SDM_SSC_EN);
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_PCLK_DIV_SEL,
|
|
FIELD_PREP(AUSPLL_FD_PCLK_DIV_SEL, cfg->pclk_div_sel));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_LFSDM_DIV,
|
|
FIELD_PREP(AUSPLL_FD_LFSDM_DIV, 1));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_LFCLK_CTRL,
|
|
FIELD_PREP(AUSPLL_FD_LFCLK_CTRL, cfg->lfclk_ctrl));
|
|
core_mask32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_VCLK_OP_DIVN,
|
|
FIELD_PREP(AUSPLL_FD_VCLK_OP_DIVN, cfg->vclk_op_divn));
|
|
core_set32(atcphy, AUSPLL_FREQ_DESC_C, AUSPLL_FD_VCLK_PRE_DIVN);
|
|
|
|
core_mask32(atcphy, AUSPLL_CLKOUT_DIV, AUSPLL_CLKOUT_PLLA_REFBUFCLK_DI,
|
|
FIELD_PREP(AUSPLL_CLKOUT_PLLA_REFBUFCLK_DI, 7));
|
|
|
|
if (cfg->plla_clkout_vreg_bypass)
|
|
core_set32(atcphy, AUSPLL_CLKOUT_DTC_VREG, AUSPLL_DTC_VREG_BYPASS);
|
|
else
|
|
core_clear32(atcphy, AUSPLL_CLKOUT_DTC_VREG, AUSPLL_DTC_VREG_BYPASS);
|
|
|
|
core_set32(atcphy, AUSPLL_BGR, AUSPLL_BGR_CTRL_AVAIL);
|
|
|
|
core_set32(atcphy, AUSPLL_CLKOUT_MASTER, AUSPLL_CLKOUT_MASTER_PCLK_DRVR_EN);
|
|
core_set32(atcphy, AUSPLL_CLKOUT_MASTER, AUSPLL_CLKOUT_MASTER_PCLK2_DRVR_EN);
|
|
core_set32(atcphy, AUSPLL_CLKOUT_MASTER, AUSPLL_CLKOUT_MASTER_REFBUFCLK_DRVR_EN);
|
|
|
|
ret = atcphy_auspll_apb_command(atcphy, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = readl_poll_timeout(atcphy->regs.core + ACIOPHY_DP_PCLK_STAT, reg,
|
|
(reg & ACIOPHY_AUSPLL_LOCK), 10, 10000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "ACIOPHY_DP_PCLK did not lock.\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = atcphy_auspll_apb_command(atcphy, 0x2800);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (mode_cfg->dp_lane[0]) {
|
|
ret = atcphy_dp_configure_lane(atcphy, APPLE_ATCPHY_LANE_0, cfg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (mode_cfg->dp_lane[1]) {
|
|
ret = atcphy_dp_configure_lane(atcphy, APPLE_ATCPHY_LANE_1, cfg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DP_PMA_BYTECLK_RESET);
|
|
core_clear32(atcphy, ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0, DP_MAC_DIV20_CLK_SEL);
|
|
|
|
atcphy->dp_link_rate = lr;
|
|
return 0;
|
|
}
|
|
|
|
static void atcphy_usb2_power_off(struct apple_atcphy *atcphy)
|
|
{
|
|
/* Disable the PHY, this clears USB2PHY_USBCTL_RUN */
|
|
writel(USB2PHY_USBCTL_ISOLATION, atcphy->regs.usb2phy + USB2PHY_USBCTL);
|
|
udelay(10);
|
|
|
|
/* Switch the PHY to low power mode */
|
|
set32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_SIDDQ);
|
|
udelay(10);
|
|
|
|
/* Enable all resets */
|
|
set32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_PORT_RESET);
|
|
udelay(10);
|
|
set32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_RESET);
|
|
udelay(10);
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_APB_RESET_N);
|
|
udelay(10);
|
|
set32(atcphy->regs.usb2phy + USB2PHY_MISCTUNE, USB2PHY_MISCTUNE_APBCLK_GATE_OFF);
|
|
set32(atcphy->regs.usb2phy + USB2PHY_MISCTUNE, USB2PHY_MISCTUNE_REFCLK_GATE_OFF);
|
|
}
|
|
|
|
static int atcphy_power_off(struct apple_atcphy *atcphy)
|
|
{
|
|
u32 reg;
|
|
int ret;
|
|
|
|
atcphy_disable_dp_aux(atcphy);
|
|
|
|
/* Enable all reset lines */
|
|
core_clear32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_PHY_RESET_N);
|
|
core_set32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_CLAMP_EN);
|
|
core_clear32(atcphy, ATCPHY_MISC, ATCPHY_MISC_RESET_N | ATCPHY_MISC_LANE_SWAP);
|
|
core_clear32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_APB_RESET_N);
|
|
|
|
core_clear32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_SLEEP_BIG);
|
|
ret = readl_poll_timeout(atcphy->regs.core + ATCPHY_POWER_STAT, reg,
|
|
!(reg & ATCPHY_POWER_SLEEP_BIG), 10, 1000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "Failed to sleep atcphy \"big\"\n");
|
|
return ret;
|
|
}
|
|
|
|
core_clear32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_SLEEP_SMALL);
|
|
ret = readl_poll_timeout(atcphy->regs.core + ATCPHY_POWER_STAT, reg,
|
|
!(reg & ATCPHY_POWER_SLEEP_SMALL), 10, 1000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "Failed to sleep atcphy \"small\"\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void atcphy_usb2_power_on(struct apple_atcphy *atcphy)
|
|
{
|
|
set32(atcphy->regs.usb2phy + USB2PHY_SIG,
|
|
USB2PHY_SIG_VBUSDET_FORCE_VAL | USB2PHY_SIG_VBUSDET_FORCE_EN |
|
|
USB2PHY_SIG_VBUSVLDEXT_FORCE_VAL | USB2PHY_SIG_VBUSVLDEXT_FORCE_EN);
|
|
udelay(10);
|
|
|
|
/* Take the PHY out of its low power state */
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_SIDDQ);
|
|
udelay(10);
|
|
|
|
/* Release reset */
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_RESET);
|
|
udelay(10);
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_PORT_RESET);
|
|
udelay(10);
|
|
set32(atcphy->regs.usb2phy + USB2PHY_CTL, USB2PHY_CTL_APB_RESET_N);
|
|
udelay(10);
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_MISCTUNE, USB2PHY_MISCTUNE_APBCLK_GATE_OFF);
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_MISCTUNE, USB2PHY_MISCTUNE_REFCLK_GATE_OFF);
|
|
|
|
/* Enable the PHY */
|
|
writel(USB2PHY_USBCTL_RUN, atcphy->regs.usb2phy + USB2PHY_USBCTL);
|
|
}
|
|
|
|
static int atcphy_power_on(struct apple_atcphy *atcphy)
|
|
{
|
|
u32 reg;
|
|
int ret;
|
|
|
|
atcphy_usb2_power_on(atcphy);
|
|
|
|
core_set32(atcphy, ATCPHY_MISC, ATCPHY_MISC_RESET_N);
|
|
|
|
core_set32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_SLEEP_SMALL);
|
|
ret = readl_poll_timeout(atcphy->regs.core + ATCPHY_POWER_STAT, reg,
|
|
reg & ATCPHY_POWER_SLEEP_SMALL, 100, 100000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "failed to wakeup atcphy \"small\"\n");
|
|
return ret;
|
|
}
|
|
|
|
core_set32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_SLEEP_BIG);
|
|
ret = readl_poll_timeout(atcphy->regs.core + ATCPHY_POWER_STAT, reg,
|
|
reg & ATCPHY_POWER_SLEEP_BIG, 100, 100000);
|
|
if (ret) {
|
|
dev_err(atcphy->dev, "failed to wakeup atcphy \"big\"\n");
|
|
return ret;
|
|
}
|
|
|
|
core_clear32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_CLAMP_EN);
|
|
core_set32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_APB_RESET_N);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_configure(struct apple_atcphy *atcphy, enum atcphy_mode mode)
|
|
{
|
|
int ret = 0;
|
|
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
if (mode == APPLE_ATCPHY_MODE_OFF) {
|
|
ret = atcphy_power_off(atcphy);
|
|
atcphy->mode = mode;
|
|
return ret;
|
|
}
|
|
|
|
ret = atcphy_power_on(atcphy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
atcphy_apply_tunables(atcphy, mode);
|
|
|
|
core_set32(atcphy, AUSPLL_FSM_CTRL, 0x1fe000);
|
|
core_set32(atcphy, AUSPLL_APB_CMD_OVERRIDE, AUSPLL_APB_CMD_OVERRIDE_UNK28);
|
|
|
|
set32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_COMMON_SMALL_OV);
|
|
udelay(10);
|
|
set32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_COMMON_BIG_OV);
|
|
udelay(10);
|
|
set32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_COMMON_CLAMP_OV);
|
|
udelay(10);
|
|
|
|
mask32(atcphy->regs.core + ACIOPHY_SLEEP_CTRL, ACIOPHY_SLEEP_CTRL_TX_SMALL_OV,
|
|
FIELD_PREP(ACIOPHY_SLEEP_CTRL_TX_SMALL_OV, 3));
|
|
udelay(10);
|
|
mask32(atcphy->regs.core + ACIOPHY_SLEEP_CTRL, ACIOPHY_SLEEP_CTRL_TX_BIG_OV,
|
|
FIELD_PREP(ACIOPHY_SLEEP_CTRL_TX_BIG_OV, 3));
|
|
udelay(10);
|
|
mask32(atcphy->regs.core + ACIOPHY_SLEEP_CTRL, ACIOPHY_SLEEP_CTRL_TX_CLAMP_OV,
|
|
FIELD_PREP(ACIOPHY_SLEEP_CTRL_TX_CLAMP_OV, 3));
|
|
udelay(10);
|
|
|
|
mask32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_RX_BIG_OV,
|
|
FIELD_PREP(ACIOPHY_CFG0_RX_BIG_OV, 3));
|
|
udelay(10);
|
|
mask32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_RX_SMALL_OV,
|
|
FIELD_PREP(ACIOPHY_CFG0_RX_SMALL_OV, 3));
|
|
udelay(10);
|
|
mask32(atcphy->regs.core + ACIOPHY_CFG0, ACIOPHY_CFG0_RX_CLAMP_OV,
|
|
FIELD_PREP(ACIOPHY_CFG0_RX_CLAMP_OV, 3));
|
|
udelay(10);
|
|
|
|
/* Setup AUX channel if DP altmode is requested */
|
|
if (atcphy_modes[mode].enable_dp_aux)
|
|
atcphy_enable_dp_aux(atcphy);
|
|
|
|
/* Enable clocks and configure lanes */
|
|
core_set32(atcphy, CIO3PLL_CLK_CTRL, CIO3PLL_CLK_PCLK_EN);
|
|
core_set32(atcphy, CIO3PLL_CLK_CTRL, CIO3PLL_CLK_REFCLK_EN);
|
|
atcphy_configure_lanes(atcphy, mode);
|
|
|
|
/* Take the USB3 PHY out of reset */
|
|
core_set32(atcphy, ATCPHY_POWER_CTRL, ATCPHY_POWER_PHY_RESET_N);
|
|
|
|
atcphy->mode = mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_usb2_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
|
{
|
|
struct apple_atcphy *atcphy = phy_get_drvdata(phy);
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
switch (mode) {
|
|
case PHY_MODE_USB_HOST:
|
|
set32(atcphy->regs.usb2phy + USB2PHY_SIG, USB2PHY_SIG_HOST);
|
|
break;
|
|
case PHY_MODE_USB_DEVICE:
|
|
clear32(atcphy->regs.usb2phy + USB2PHY_SIG, USB2PHY_SIG_HOST);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct phy_ops apple_atc_usb2_phy_ops = {
|
|
.owner = THIS_MODULE,
|
|
.set_mode = atcphy_usb2_set_mode,
|
|
};
|
|
|
|
static int atcphy_usb3_power_off(struct phy *phy)
|
|
{
|
|
struct apple_atcphy *atcphy = phy_get_drvdata(phy);
|
|
int ret;
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
ret = atcphy_configure_pipehandler_dummy(atcphy);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Failed to switch pipe to dummy: %d", ret);
|
|
|
|
atcphy->pipehandler_up = false;
|
|
|
|
if (atcphy->mode != APPLE_ATCPHY_MODE_OFF)
|
|
atcphy_configure(atcphy, APPLE_ATCPHY_MODE_OFF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_usb3_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
|
{
|
|
struct apple_atcphy *atcphy = phy_get_drvdata(phy);
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
/*
|
|
* We may get multiple calls to set_mode (for host mode e.g. at least one from the dwc3 glue
|
|
* driver and then another one from the generic xhci code) but must only configure the
|
|
* PIPE handler once.
|
|
*/
|
|
if (atcphy->pipehandler_up)
|
|
return 0;
|
|
|
|
switch (mode) {
|
|
case PHY_MODE_USB_HOST:
|
|
return atcphy_configure_pipehandler(atcphy, true);
|
|
case PHY_MODE_USB_DEVICE:
|
|
return atcphy_configure_pipehandler(atcphy, false);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static const struct phy_ops apple_atc_usb3_phy_ops = {
|
|
.owner = THIS_MODULE,
|
|
.power_off = atcphy_usb3_power_off,
|
|
.set_mode = atcphy_usb3_set_mode,
|
|
};
|
|
|
|
static int atcphy_dpphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
|
{
|
|
/* Nothing to do here since the setup already happened in mux_set */
|
|
if (mode == PHY_MODE_DP && submode == 0)
|
|
return 0;
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int atcphy_dpphy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
|
union phy_configure_opts *opts_)
|
|
{
|
|
struct phy_configure_opts_dp *opts = &opts_->dp;
|
|
struct apple_atcphy *atcphy = phy_get_drvdata(phy);
|
|
|
|
if (mode != PHY_MODE_DP)
|
|
return -EINVAL;
|
|
if (submode != 0)
|
|
return -EINVAL;
|
|
|
|
switch (atcphy->mode) {
|
|
case APPLE_ATCPHY_MODE_USB3_DP:
|
|
opts->lanes = 2;
|
|
break;
|
|
case APPLE_ATCPHY_MODE_DP:
|
|
opts->lanes = 4;
|
|
break;
|
|
default:
|
|
opts->lanes = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_dpphy_configure(struct phy *phy, union phy_configure_opts *opts_)
|
|
{
|
|
struct phy_configure_opts_dp *opts = &opts_->dp;
|
|
struct apple_atcphy *atcphy = phy_get_drvdata(phy);
|
|
enum atcphy_dp_link_rate link_rate;
|
|
|
|
if (opts->set_voltages)
|
|
return -EINVAL;
|
|
if (opts->set_lanes)
|
|
return -EINVAL;
|
|
|
|
if (opts->set_rate) {
|
|
switch (opts->link_rate) {
|
|
case 1620:
|
|
link_rate = ATCPHY_DP_LINK_RATE_RBR;
|
|
break;
|
|
case 2700:
|
|
link_rate = ATCPHY_DP_LINK_RATE_HBR;
|
|
break;
|
|
case 5400:
|
|
link_rate = ATCPHY_DP_LINK_RATE_HBR2;
|
|
break;
|
|
case 8100:
|
|
link_rate = ATCPHY_DP_LINK_RATE_HBR3;
|
|
break;
|
|
case 0:
|
|
return 0;
|
|
default:
|
|
dev_err(atcphy->dev, "Unsupported link rate: %d\n", opts->link_rate);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return atcphy_dp_configure(atcphy, link_rate);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct phy_ops apple_atc_dp_phy_ops = {
|
|
.owner = THIS_MODULE,
|
|
.configure = atcphy_dpphy_configure,
|
|
.validate = atcphy_dpphy_validate,
|
|
.set_mode = atcphy_dpphy_set_mode,
|
|
};
|
|
|
|
static struct phy *atcphy_xlate(struct device *dev, const struct of_phandle_args *args)
|
|
{
|
|
struct apple_atcphy *atcphy = dev_get_drvdata(dev);
|
|
|
|
switch (args->args[0]) {
|
|
case PHY_TYPE_USB2:
|
|
return atcphy->phys.usb2;
|
|
case PHY_TYPE_USB3:
|
|
return atcphy->phys.usb3;
|
|
case PHY_TYPE_DP:
|
|
return atcphy->phys.dp;
|
|
}
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
static int atcphy_probe_phy(struct apple_atcphy *atcphy)
|
|
{
|
|
struct {
|
|
struct phy **phy;
|
|
const struct phy_ops *ops;
|
|
} phys[] = {
|
|
{ &atcphy->phys.usb2, &apple_atc_usb2_phy_ops },
|
|
{ &atcphy->phys.usb3, &apple_atc_usb3_phy_ops },
|
|
{ &atcphy->phys.dp, &apple_atc_dp_phy_ops },
|
|
};
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(phys); i++) {
|
|
*phys[i].phy = devm_phy_create(atcphy->dev, NULL, phys[i].ops);
|
|
if (IS_ERR(*phys[i].phy))
|
|
return PTR_ERR(*phys[i].phy);
|
|
phy_set_drvdata(*phys[i].phy, atcphy);
|
|
}
|
|
|
|
atcphy->phy_provider = devm_of_phy_provider_register(atcphy->dev, atcphy_xlate);
|
|
if (IS_ERR(atcphy->phy_provider))
|
|
return PTR_ERR(atcphy->phy_provider);
|
|
return 0;
|
|
}
|
|
|
|
static void _atcphy_dwc3_reset_assert(struct apple_atcphy *atcphy)
|
|
{
|
|
lockdep_assert_held(&atcphy->lock);
|
|
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_AON_GEN, PIPEHANDLER_AON_GEN_DWC3_RESET_N);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_AON_GEN,
|
|
PIPEHANDLER_AON_GEN_DWC3_FORCE_CLAMP_EN);
|
|
}
|
|
|
|
static int atcphy_dwc3_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
|
{
|
|
struct apple_atcphy *atcphy = container_of(rcdev, struct apple_atcphy, rcdev);
|
|
int ret;
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
_atcphy_dwc3_reset_assert(atcphy);
|
|
|
|
if (atcphy->pipehandler_up) {
|
|
ret = atcphy_configure_pipehandler_dummy(atcphy);
|
|
if (ret)
|
|
dev_warn(atcphy->dev, "Failed to switch PIPE to dummy: %d\n", ret);
|
|
else
|
|
atcphy->pipehandler_up = false;
|
|
}
|
|
|
|
atcphy_usb2_power_off(atcphy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_dwc3_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
|
{
|
|
struct apple_atcphy *atcphy = container_of(rcdev, struct apple_atcphy, rcdev);
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
clear32(atcphy->regs.pipehandler + PIPEHANDLER_AON_GEN,
|
|
PIPEHANDLER_AON_GEN_DWC3_FORCE_CLAMP_EN);
|
|
set32(atcphy->regs.pipehandler + PIPEHANDLER_AON_GEN, PIPEHANDLER_AON_GEN_DWC3_RESET_N);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct reset_control_ops atcphy_dwc3_reset_ops = {
|
|
.assert = atcphy_dwc3_reset_assert,
|
|
.deassert = atcphy_dwc3_reset_deassert,
|
|
};
|
|
|
|
static int atcphy_reset_xlate(struct reset_controller_dev *rcdev,
|
|
const struct of_phandle_args *reset_spec)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_probe_rcdev(struct apple_atcphy *atcphy)
|
|
{
|
|
atcphy->rcdev.owner = THIS_MODULE;
|
|
atcphy->rcdev.nr_resets = 1;
|
|
atcphy->rcdev.ops = &atcphy_dwc3_reset_ops;
|
|
atcphy->rcdev.of_node = atcphy->dev->of_node;
|
|
atcphy->rcdev.of_reset_n_cells = 0;
|
|
atcphy->rcdev.of_xlate = atcphy_reset_xlate;
|
|
|
|
return devm_reset_controller_register(atcphy->dev, &atcphy->rcdev);
|
|
}
|
|
|
|
static int atcphy_sw_set(struct typec_switch_dev *sw, enum typec_orientation orientation)
|
|
{
|
|
struct apple_atcphy *atcphy = typec_switch_get_drvdata(sw);
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
switch (orientation) {
|
|
case TYPEC_ORIENTATION_NONE:
|
|
break;
|
|
case TYPEC_ORIENTATION_NORMAL:
|
|
atcphy->swap_lanes = false;
|
|
break;
|
|
case TYPEC_ORIENTATION_REVERSE:
|
|
atcphy->swap_lanes = true;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_probe_switch(struct apple_atcphy *atcphy)
|
|
{
|
|
struct typec_switch_desc sw_desc = {
|
|
.drvdata = atcphy,
|
|
.fwnode = atcphy->dev->fwnode,
|
|
.set = atcphy_sw_set,
|
|
};
|
|
|
|
return PTR_ERR_OR_ZERO(typec_switch_register(atcphy->dev, &sw_desc));
|
|
}
|
|
|
|
static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
|
|
{
|
|
struct apple_atcphy *atcphy = typec_mux_get_drvdata(mux);
|
|
enum atcphy_mode target_mode;
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
if (state->mode == TYPEC_STATE_SAFE) {
|
|
target_mode = APPLE_ATCPHY_MODE_OFF;
|
|
} else if (state->mode == TYPEC_STATE_USB) {
|
|
target_mode = APPLE_ATCPHY_MODE_USB3;
|
|
} else if (!state->alt && state->mode == TYPEC_MODE_USB4) {
|
|
struct enter_usb_data *data = state->data;
|
|
u32 eudo_usb_mode = FIELD_GET(EUDO_USB_MODE_MASK, data->eudo);
|
|
|
|
switch (eudo_usb_mode) {
|
|
case EUDO_USB_MODE_USB2:
|
|
target_mode = APPLE_ATCPHY_MODE_USB2;
|
|
break;
|
|
case EUDO_USB_MODE_USB3:
|
|
target_mode = APPLE_ATCPHY_MODE_USB3;
|
|
break;
|
|
case EUDO_USB_MODE_USB4:
|
|
target_mode = APPLE_ATCPHY_MODE_USB4;
|
|
break;
|
|
default:
|
|
dev_warn(atcphy->dev, "Unsupported EUDO USB mode: 0x%x.\n", eudo_usb_mode);
|
|
target_mode = APPLE_ATCPHY_MODE_OFF;
|
|
}
|
|
} else if (state->alt && state->alt->svid == USB_TYPEC_TBT_SID) {
|
|
target_mode = APPLE_ATCPHY_MODE_TBT;
|
|
} else if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
|
|
switch (state->mode) {
|
|
case TYPEC_DP_STATE_C:
|
|
case TYPEC_DP_STATE_E:
|
|
target_mode = APPLE_ATCPHY_MODE_DP;
|
|
break;
|
|
case TYPEC_DP_STATE_D:
|
|
target_mode = APPLE_ATCPHY_MODE_USB3_DP;
|
|
break;
|
|
default:
|
|
dev_err(atcphy->dev,
|
|
"Unsupported DP pin assignment: 0x%lx, your connected device will not work.\n",
|
|
state->mode);
|
|
target_mode = APPLE_ATCPHY_MODE_OFF;
|
|
}
|
|
} else if (state->alt) {
|
|
dev_err(atcphy->dev,
|
|
"Unknown alternate mode SVID: 0x%x, your connected device will not work.\n",
|
|
state->alt->svid);
|
|
target_mode = APPLE_ATCPHY_MODE_OFF;
|
|
} else {
|
|
dev_err(atcphy->dev, "Unknown mode: 0x%lx, your connected device will not work.\n",
|
|
state->mode);
|
|
target_mode = APPLE_ATCPHY_MODE_OFF;
|
|
}
|
|
|
|
if (atcphy->mode == target_mode)
|
|
return 0;
|
|
|
|
/*
|
|
* If the pipehandler is still/already up here there's a bug somewhere so make sure to
|
|
* complain loudly. We can still try to switch modes and hope for the best though,
|
|
* in the worst case the hardware will fall back to USB2-only.
|
|
*/
|
|
WARN_ON_ONCE(atcphy->pipehandler_up);
|
|
return atcphy_configure(atcphy, target_mode);
|
|
}
|
|
|
|
static int atcphy_probe_mux(struct apple_atcphy *atcphy)
|
|
{
|
|
struct typec_mux_desc mux_desc = {
|
|
.drvdata = atcphy,
|
|
.fwnode = atcphy->dev->fwnode,
|
|
.set = atcphy_mux_set,
|
|
};
|
|
|
|
return PTR_ERR_OR_ZERO(typec_mux_register(atcphy->dev, &mux_desc));
|
|
}
|
|
|
|
static int atcphy_load_tunables(struct apple_atcphy *atcphy)
|
|
{
|
|
struct {
|
|
const char *dt_name;
|
|
struct apple_tunable **tunable;
|
|
struct resource *res;
|
|
} tunables[] = {
|
|
{ "apple,tunable-axi2af", &atcphy->tunables.axi2af, atcphy->res.axi2af },
|
|
{ "apple,tunable-common-a", &atcphy->tunables.common[0], atcphy->res.core },
|
|
{ "apple,tunable-common-b", &atcphy->tunables.common[1], atcphy->res.core },
|
|
{ "apple,tunable-lane0-usb", &atcphy->tunables.lane_usb3[0], atcphy->res.core },
|
|
{ "apple,tunable-lane1-usb", &atcphy->tunables.lane_usb3[1], atcphy->res.core },
|
|
{ "apple,tunable-lane0-cio", &atcphy->tunables.lane_usb4[0], atcphy->res.core },
|
|
{ "apple,tunable-lane1-cio", &atcphy->tunables.lane_usb4[1], atcphy->res.core },
|
|
{ "apple,tunable-lane0-dp", &atcphy->tunables.lane_dp[0], atcphy->res.core },
|
|
{ "apple,tunable-lane1-dp", &atcphy->tunables.lane_dp[1], atcphy->res.core },
|
|
};
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(tunables); i++) {
|
|
*tunables[i].tunable = devm_apple_tunable_parse(
|
|
atcphy->dev, atcphy->np, tunables[i].dt_name, tunables[i].res);
|
|
if (IS_ERR(*tunables[i].tunable)) {
|
|
dev_err(atcphy->dev, "Failed to read tunable %s: %ld\n",
|
|
tunables[i].dt_name, PTR_ERR(*tunables[i].tunable));
|
|
return PTR_ERR(*tunables[i].tunable);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
|
|
{
|
|
struct {
|
|
const char *name;
|
|
void __iomem **addr;
|
|
struct resource **res;
|
|
} resources[] = {
|
|
{ "core", &atcphy->regs.core, &atcphy->res.core },
|
|
{ "lpdptx", &atcphy->regs.lpdptx, NULL },
|
|
{ "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
|
|
{ "usb2phy", &atcphy->regs.usb2phy, NULL },
|
|
{ "pipehandler", &atcphy->regs.pipehandler, NULL },
|
|
};
|
|
struct resource *res;
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(resources); i++) {
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
|
|
*resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(resources[i].addr))
|
|
return dev_err_probe(atcphy->dev, PTR_ERR(resources[i].addr),
|
|
"Unable to map %s regs", resources[i].name);
|
|
|
|
if (resources[i].res)
|
|
*resources[i].res = res;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_probe_finalize(struct apple_atcphy *atcphy)
|
|
{
|
|
int ret;
|
|
|
|
guard(mutex)(&atcphy->lock);
|
|
|
|
/* Reset dwc3 on probe, let dwc3 (consumer) deassert it */
|
|
_atcphy_dwc3_reset_assert(atcphy);
|
|
|
|
/* Reset atcphy to clear any state potentially left by the bootloader */
|
|
atcphy_usb2_power_off(atcphy);
|
|
atcphy_power_off(atcphy);
|
|
atcphy_setup_pipehandler(atcphy);
|
|
|
|
ret = atcphy_probe_rcdev(atcphy);
|
|
if (ret)
|
|
return dev_err_probe(atcphy->dev, ret, "Probing rcdev failed");
|
|
ret = atcphy_probe_mux(atcphy);
|
|
if (ret)
|
|
return dev_err_probe(atcphy->dev, ret, "Probing mux failed");
|
|
ret = atcphy_probe_switch(atcphy);
|
|
if (ret)
|
|
return dev_err_probe(atcphy->dev, ret, "Probing switch failed");
|
|
ret = atcphy_probe_phy(atcphy);
|
|
if (ret)
|
|
return dev_err_probe(atcphy->dev, ret, "Probing phy failed");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atcphy_probe(struct platform_device *pdev)
|
|
{
|
|
struct apple_atcphy *atcphy;
|
|
struct device *dev = &pdev->dev;
|
|
int ret;
|
|
|
|
atcphy = devm_kzalloc(&pdev->dev, sizeof(*atcphy), GFP_KERNEL);
|
|
if (!atcphy)
|
|
return -ENOMEM;
|
|
|
|
atcphy->dev = dev;
|
|
atcphy->np = dev->of_node;
|
|
mutex_init(&atcphy->lock);
|
|
platform_set_drvdata(pdev, atcphy);
|
|
|
|
ret = atcphy_map_resources(pdev, atcphy);
|
|
if (ret)
|
|
return ret;
|
|
ret = atcphy_load_tunables(atcphy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
atcphy->mode = APPLE_ATCPHY_MODE_OFF;
|
|
atcphy->pipehandler_up = false;
|
|
|
|
return atcphy_probe_finalize(atcphy);
|
|
}
|
|
|
|
static const struct of_device_id atcphy_match[] = {
|
|
{ .compatible = "apple,t8103-atcphy" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, atcphy_match);
|
|
|
|
static struct platform_driver atcphy_driver = {
|
|
.driver = {
|
|
.name = "phy-apple-atc",
|
|
.of_match_table = atcphy_match,
|
|
},
|
|
.probe = atcphy_probe,
|
|
};
|
|
module_platform_driver(atcphy_driver);
|
|
|
|
MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
|
|
MODULE_DESCRIPTION("Apple Type-C PHY driver");
|
|
MODULE_LICENSE("GPL");
|