171e2f5c5SKishon Vijay Abraham I // SPDX-License-Identifier: GPL-2.0 271e2f5c5SKishon Vijay Abraham I /** 371e2f5c5SKishon Vijay Abraham I * PCIe SERDES driver for AM654x SoC 471e2f5c5SKishon Vijay Abraham I * 571e2f5c5SKishon Vijay Abraham I * Copyright (C) 2018 - 2019 Texas Instruments Incorporated - http://www.ti.com/ 671e2f5c5SKishon Vijay Abraham I * Author: Kishon Vijay Abraham I <kishon@ti.com> 771e2f5c5SKishon Vijay Abraham I */ 871e2f5c5SKishon Vijay Abraham I 971e2f5c5SKishon Vijay Abraham I #include <dt-bindings/phy/phy.h> 1071e2f5c5SKishon Vijay Abraham I #include <linux/clk.h> 1171e2f5c5SKishon Vijay Abraham I #include <linux/clk-provider.h> 1271e2f5c5SKishon Vijay Abraham I #include <linux/delay.h> 1371e2f5c5SKishon Vijay Abraham I #include <linux/module.h> 1471e2f5c5SKishon Vijay Abraham I #include <linux/mfd/syscon.h> 1571e2f5c5SKishon Vijay Abraham I #include <linux/mux/consumer.h> 1671e2f5c5SKishon Vijay Abraham I #include <linux/of_address.h> 1771e2f5c5SKishon Vijay Abraham I #include <linux/phy/phy.h> 1871e2f5c5SKishon Vijay Abraham I #include <linux/platform_device.h> 1971e2f5c5SKishon Vijay Abraham I #include <linux/pm_runtime.h> 2071e2f5c5SKishon Vijay Abraham I #include <linux/regmap.h> 2171e2f5c5SKishon Vijay Abraham I 22f78c40aaSSekhar Nori #define CMU_R004 0x4 23f78c40aaSSekhar Nori #define CMU_R060 0x60 2471e2f5c5SKishon Vijay Abraham I #define CMU_R07C 0x7c 25f78c40aaSSekhar Nori #define CMU_R088 0x88 26f78c40aaSSekhar Nori #define CMU_R0D0 0xd0 27f78c40aaSSekhar Nori #define CMU_R0E8 0xe8 2871e2f5c5SKishon Vijay Abraham I 29f78c40aaSSekhar Nori #define LANE_R048 0x248 30f78c40aaSSekhar Nori #define LANE_R058 0x258 31f78c40aaSSekhar Nori #define LANE_R06c 0x26c 32f78c40aaSSekhar Nori #define LANE_R070 0x270 33f78c40aaSSekhar Nori #define LANE_R070 0x270 34f78c40aaSSekhar Nori #define LANE_R19C 0x39c 35f78c40aaSSekhar Nori 36f78c40aaSSekhar Nori #define COMLANE_R004 0xa04 3771e2f5c5SKishon Vijay Abraham I #define COMLANE_R138 0xb38 38c3e60e5aSSekhar Nori #define VERSION_VAL 0x70 3971e2f5c5SKishon Vijay Abraham I 4071e2f5c5SKishon Vijay Abraham I #define COMLANE_R190 0xb90 4171e2f5c5SKishon Vijay Abraham I #define COMLANE_R194 0xb94 4271e2f5c5SKishon Vijay Abraham I 43f78c40aaSSekhar Nori #define COMRXEQ_R004 0x1404 44f78c40aaSSekhar Nori #define COMRXEQ_R008 0x1408 45f78c40aaSSekhar Nori #define COMRXEQ_R00C 0x140c 46f78c40aaSSekhar Nori #define COMRXEQ_R014 0x1414 47f78c40aaSSekhar Nori #define COMRXEQ_R018 0x1418 48f78c40aaSSekhar Nori #define COMRXEQ_R01C 0x141c 49f78c40aaSSekhar Nori #define COMRXEQ_R04C 0x144c 50f78c40aaSSekhar Nori #define COMRXEQ_R088 0x1488 51f78c40aaSSekhar Nori #define COMRXEQ_R094 0x1494 52f78c40aaSSekhar Nori #define COMRXEQ_R098 0x1498 53f78c40aaSSekhar Nori 5471e2f5c5SKishon Vijay Abraham I #define SERDES_CTRL 0x1fd0 5571e2f5c5SKishon Vijay Abraham I 5671e2f5c5SKishon Vijay Abraham I #define WIZ_LANEXCTL_STS 0x1fe0 5771e2f5c5SKishon Vijay Abraham I #define TX0_DISABLE_STATE 0x4 5871e2f5c5SKishon Vijay Abraham I #define TX0_SLEEP_STATE 0x5 5971e2f5c5SKishon Vijay Abraham I #define TX0_SNOOZE_STATE 0x6 6071e2f5c5SKishon Vijay Abraham I #define TX0_ENABLE_STATE 0x7 6171e2f5c5SKishon Vijay Abraham I 6271e2f5c5SKishon Vijay Abraham I #define RX0_DISABLE_STATE 0x4 6371e2f5c5SKishon Vijay Abraham I #define RX0_SLEEP_STATE 0x5 6471e2f5c5SKishon Vijay Abraham I #define RX0_SNOOZE_STATE 0x6 6571e2f5c5SKishon Vijay Abraham I #define RX0_ENABLE_STATE 0x7 6671e2f5c5SKishon Vijay Abraham I 6771e2f5c5SKishon Vijay Abraham I #define WIZ_PLL_CTRL 0x1ff4 6871e2f5c5SKishon Vijay Abraham I #define PLL_DISABLE_STATE 0x4 6971e2f5c5SKishon Vijay Abraham I #define PLL_SLEEP_STATE 0x5 7071e2f5c5SKishon Vijay Abraham I #define PLL_SNOOZE_STATE 0x6 7171e2f5c5SKishon Vijay Abraham I #define PLL_ENABLE_STATE 0x7 7271e2f5c5SKishon Vijay Abraham I 7371e2f5c5SKishon Vijay Abraham I #define PLL_LOCK_TIME 100000 /* in microseconds */ 7471e2f5c5SKishon Vijay Abraham I #define SLEEP_TIME 100 /* in microseconds */ 7571e2f5c5SKishon Vijay Abraham I 7671e2f5c5SKishon Vijay Abraham I #define LANE_USB3 0x0 7771e2f5c5SKishon Vijay Abraham I #define LANE_PCIE0_LANE0 0x1 7871e2f5c5SKishon Vijay Abraham I 7971e2f5c5SKishon Vijay Abraham I #define LANE_PCIE1_LANE0 0x0 8071e2f5c5SKishon Vijay Abraham I #define LANE_PCIE0_LANE1 0x1 8171e2f5c5SKishon Vijay Abraham I 8271e2f5c5SKishon Vijay Abraham I #define SERDES_NUM_CLOCKS 3 8371e2f5c5SKishon Vijay Abraham I 847e7b8ca6SRoger Quadros #define AM654_SERDES_CTRL_CLKSEL_MASK GENMASK(7, 4) 857e7b8ca6SRoger Quadros #define AM654_SERDES_CTRL_CLKSEL_SHIFT 4 867e7b8ca6SRoger Quadros 8771e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux { 8871e2f5c5SKishon Vijay Abraham I struct clk_hw hw; 8971e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 9071e2f5c5SKishon Vijay Abraham I unsigned int reg; 917e7b8ca6SRoger Quadros int clk_id; 9271e2f5c5SKishon Vijay Abraham I struct clk_init_data clk_data; 9371e2f5c5SKishon Vijay Abraham I }; 9471e2f5c5SKishon Vijay Abraham I 9571e2f5c5SKishon Vijay Abraham I #define to_serdes_am654_clk_mux(_hw) \ 9671e2f5c5SKishon Vijay Abraham I container_of(_hw, struct serdes_am654_clk_mux, hw) 9771e2f5c5SKishon Vijay Abraham I 980cb5ebc7SRikard Falkeborn static const struct regmap_config serdes_am654_regmap_config = { 9971e2f5c5SKishon Vijay Abraham I .reg_bits = 32, 10071e2f5c5SKishon Vijay Abraham I .val_bits = 32, 10171e2f5c5SKishon Vijay Abraham I .reg_stride = 4, 10271e2f5c5SKishon Vijay Abraham I .fast_io = true, 10324dcb6a6SRoger Quadros .max_register = 0x1ffc, 10471e2f5c5SKishon Vijay Abraham I }; 10571e2f5c5SKishon Vijay Abraham I 106c3e60e5aSSekhar Nori enum serdes_am654_fields { 107f78c40aaSSekhar Nori /* CMU PLL Control */ 108f78c40aaSSekhar Nori CMU_PLL_CTRL, 109f78c40aaSSekhar Nori 110f78c40aaSSekhar Nori LANE_PLL_CTRL_RXEQ_RXIDLE, 111f78c40aaSSekhar Nori 112f78c40aaSSekhar Nori /* CMU VCO bias current and VREG setting */ 113f78c40aaSSekhar Nori AHB_PMA_CM_VCO_VBIAS_VREG, 114f78c40aaSSekhar Nori AHB_PMA_CM_VCO_BIAS_VREG, 115f78c40aaSSekhar Nori 116f78c40aaSSekhar Nori AHB_PMA_CM_SR, 117f78c40aaSSekhar Nori AHB_SSC_GEN_Z_O_20_13, 118f78c40aaSSekhar Nori 119f78c40aaSSekhar Nori /* AHB PMA Lane Configuration */ 120f78c40aaSSekhar Nori AHB_PMA_LN_AGC_THSEL_VREGH, 121f78c40aaSSekhar Nori 122f78c40aaSSekhar Nori /* AGC and Signal detect threshold for Gen3 */ 123f78c40aaSSekhar Nori AHB_PMA_LN_GEN3_AGC_SD_THSEL, 124f78c40aaSSekhar Nori 125f78c40aaSSekhar Nori AHB_PMA_LN_RX_SELR_GEN3, 126f78c40aaSSekhar Nori AHB_PMA_LN_TX_DRV, 127f78c40aaSSekhar Nori 128c3e60e5aSSekhar Nori /* CMU Master Reset */ 129c3e60e5aSSekhar Nori CMU_MASTER_CDN, 130f78c40aaSSekhar Nori 131f78c40aaSSekhar Nori /* P2S ring buffer initial startup pointer difference */ 132f78c40aaSSekhar Nori P2S_RBUF_PTR_DIFF, 133f78c40aaSSekhar Nori 134c3e60e5aSSekhar Nori CONFIG_VERSION, 135c3e60e5aSSekhar Nori 136c3e60e5aSSekhar Nori /* Lane 1 Master Reset */ 137c3e60e5aSSekhar Nori L1_MASTER_CDN, 138c3e60e5aSSekhar Nori 139c3e60e5aSSekhar Nori /* CMU OK Status */ 140c3e60e5aSSekhar Nori CMU_OK_I_0, 141c3e60e5aSSekhar Nori 142f78c40aaSSekhar Nori /* Mid-speed initial calibration control */ 143f78c40aaSSekhar Nori COMRXEQ_MS_INIT_CTRL_7_0, 144f78c40aaSSekhar Nori 145f78c40aaSSekhar Nori /* High-speed initial calibration control */ 146f78c40aaSSekhar Nori COMRXEQ_HS_INIT_CAL_7_0, 147f78c40aaSSekhar Nori 148f78c40aaSSekhar Nori /* Mid-speed recalibration control */ 149f78c40aaSSekhar Nori COMRXEQ_MS_RECAL_CTRL_7_0, 150f78c40aaSSekhar Nori 151f78c40aaSSekhar Nori /* High-speed recalibration control */ 152f78c40aaSSekhar Nori COMRXEQ_HS_RECAL_CTRL_7_0, 153f78c40aaSSekhar Nori 154f78c40aaSSekhar Nori /* ATT configuration */ 155f78c40aaSSekhar Nori COMRXEQ_CSR_ATT_CONFIG, 156f78c40aaSSekhar Nori 157f78c40aaSSekhar Nori /* Edge based boost adaptation window length */ 158f78c40aaSSekhar Nori COMRXEQ_CSR_EBSTADAPT_WIN_LEN, 159f78c40aaSSekhar Nori 160f78c40aaSSekhar Nori /* COMRXEQ control 3 & 4 */ 161f78c40aaSSekhar Nori COMRXEQ_CTRL_3_4, 162f78c40aaSSekhar Nori 163f78c40aaSSekhar Nori /* COMRXEQ control 14, 15 and 16*/ 164f78c40aaSSekhar Nori COMRXEQ_CTRL_14_15_16, 165f78c40aaSSekhar Nori 166f78c40aaSSekhar Nori /* Threshold for errors in pattern data */ 167f78c40aaSSekhar Nori COMRXEQ_CSR_DLEV_ERR_THRESH, 168f78c40aaSSekhar Nori 169f78c40aaSSekhar Nori /* COMRXEQ control 25 */ 170f78c40aaSSekhar Nori COMRXEQ_CTRL_25, 171f78c40aaSSekhar Nori 172f78c40aaSSekhar Nori /* Mid-speed rate change calibration control */ 173f78c40aaSSekhar Nori CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O, 174f78c40aaSSekhar Nori 175f78c40aaSSekhar Nori /* High-speed rate change calibration control */ 176f78c40aaSSekhar Nori COMRXEQ_HS_RCHANGE_CTRL_7_0, 177f78c40aaSSekhar Nori 178c3e60e5aSSekhar Nori /* Serdes reset */ 179c3e60e5aSSekhar Nori POR_EN, 180c3e60e5aSSekhar Nori 181c3e60e5aSSekhar Nori /* Tx Enable Value */ 182c3e60e5aSSekhar Nori TX0_ENABLE, 183c3e60e5aSSekhar Nori 184c3e60e5aSSekhar Nori /* Rx Enable Value */ 185c3e60e5aSSekhar Nori RX0_ENABLE, 186c3e60e5aSSekhar Nori 187c3e60e5aSSekhar Nori /* PLL Enable Value */ 188c3e60e5aSSekhar Nori PLL_ENABLE, 189c3e60e5aSSekhar Nori 190c3e60e5aSSekhar Nori /* PLL ready for use */ 191c3e60e5aSSekhar Nori PLL_OK, 192c3e60e5aSSekhar Nori 193c3e60e5aSSekhar Nori /* sentinel */ 194c3e60e5aSSekhar Nori MAX_FIELDS 195c3e60e5aSSekhar Nori 196c3e60e5aSSekhar Nori }; 197c3e60e5aSSekhar Nori 198c3e60e5aSSekhar Nori static const struct reg_field serdes_am654_reg_fields[] = { 199f78c40aaSSekhar Nori [CMU_PLL_CTRL] = REG_FIELD(CMU_R004, 8, 15), 200f78c40aaSSekhar Nori [AHB_PMA_CM_VCO_VBIAS_VREG] = REG_FIELD(CMU_R060, 8, 15), 201f78c40aaSSekhar Nori [CMU_MASTER_CDN] = REG_FIELD(CMU_R07C, 24, 31), 202f78c40aaSSekhar Nori [AHB_PMA_CM_VCO_BIAS_VREG] = REG_FIELD(CMU_R088, 24, 31), 203f78c40aaSSekhar Nori [AHB_PMA_CM_SR] = REG_FIELD(CMU_R0D0, 24, 31), 204f78c40aaSSekhar Nori [AHB_SSC_GEN_Z_O_20_13] = REG_FIELD(CMU_R0E8, 8, 15), 205f78c40aaSSekhar Nori [LANE_PLL_CTRL_RXEQ_RXIDLE] = REG_FIELD(LANE_R048, 8, 15), 206f78c40aaSSekhar Nori [AHB_PMA_LN_AGC_THSEL_VREGH] = REG_FIELD(LANE_R058, 16, 23), 207f78c40aaSSekhar Nori [AHB_PMA_LN_GEN3_AGC_SD_THSEL] = REG_FIELD(LANE_R06c, 0, 7), 208f78c40aaSSekhar Nori [AHB_PMA_LN_RX_SELR_GEN3] = REG_FIELD(LANE_R070, 16, 23), 209f78c40aaSSekhar Nori [AHB_PMA_LN_TX_DRV] = REG_FIELD(LANE_R19C, 16, 23), 210f78c40aaSSekhar Nori [P2S_RBUF_PTR_DIFF] = REG_FIELD(COMLANE_R004, 0, 7), 211c3e60e5aSSekhar Nori [CONFIG_VERSION] = REG_FIELD(COMLANE_R138, 16, 23), 212f78c40aaSSekhar Nori [L1_MASTER_CDN] = REG_FIELD(COMLANE_R190, 8, 15), 213c3e60e5aSSekhar Nori [CMU_OK_I_0] = REG_FIELD(COMLANE_R194, 19, 19), 214f78c40aaSSekhar Nori [COMRXEQ_MS_INIT_CTRL_7_0] = REG_FIELD(COMRXEQ_R004, 24, 31), 215f78c40aaSSekhar Nori [COMRXEQ_HS_INIT_CAL_7_0] = REG_FIELD(COMRXEQ_R008, 0, 7), 216f78c40aaSSekhar Nori [COMRXEQ_MS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 8, 15), 217f78c40aaSSekhar Nori [COMRXEQ_HS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 16, 23), 218f78c40aaSSekhar Nori [COMRXEQ_CSR_ATT_CONFIG] = REG_FIELD(COMRXEQ_R014, 16, 23), 219f78c40aaSSekhar Nori [COMRXEQ_CSR_EBSTADAPT_WIN_LEN] = REG_FIELD(COMRXEQ_R018, 16, 23), 220f78c40aaSSekhar Nori [COMRXEQ_CTRL_3_4] = REG_FIELD(COMRXEQ_R01C, 8, 15), 221f78c40aaSSekhar Nori [COMRXEQ_CTRL_14_15_16] = REG_FIELD(COMRXEQ_R04C, 0, 7), 222f78c40aaSSekhar Nori [COMRXEQ_CSR_DLEV_ERR_THRESH] = REG_FIELD(COMRXEQ_R088, 16, 23), 223f78c40aaSSekhar Nori [COMRXEQ_CTRL_25] = REG_FIELD(COMRXEQ_R094, 24, 31), 224f78c40aaSSekhar Nori [CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O] = REG_FIELD(COMRXEQ_R098, 8, 15), 225f78c40aaSSekhar Nori [COMRXEQ_HS_RCHANGE_CTRL_7_0] = REG_FIELD(COMRXEQ_R098, 16, 23), 226c3e60e5aSSekhar Nori [POR_EN] = REG_FIELD(SERDES_CTRL, 29, 29), 227c3e60e5aSSekhar Nori [TX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31), 228c3e60e5aSSekhar Nori [RX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15), 229c3e60e5aSSekhar Nori [PLL_ENABLE] = REG_FIELD(WIZ_PLL_CTRL, 29, 31), 230c3e60e5aSSekhar Nori [PLL_OK] = REG_FIELD(WIZ_PLL_CTRL, 28, 28), 231c3e60e5aSSekhar Nori }; 23271e2f5c5SKishon Vijay Abraham I 23371e2f5c5SKishon Vijay Abraham I struct serdes_am654 { 23471e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 235c3e60e5aSSekhar Nori struct regmap_field *fields[MAX_FIELDS]; 23671e2f5c5SKishon Vijay Abraham I 23771e2f5c5SKishon Vijay Abraham I struct device *dev; 23871e2f5c5SKishon Vijay Abraham I struct mux_control *control; 23971e2f5c5SKishon Vijay Abraham I bool busy; 24071e2f5c5SKishon Vijay Abraham I u32 type; 24171e2f5c5SKishon Vijay Abraham I struct device_node *of_node; 24271e2f5c5SKishon Vijay Abraham I struct clk_onecell_data clk_data; 24371e2f5c5SKishon Vijay Abraham I struct clk *clks[SERDES_NUM_CLOCKS]; 24471e2f5c5SKishon Vijay Abraham I }; 24571e2f5c5SKishon Vijay Abraham I 24671e2f5c5SKishon Vijay Abraham I static int serdes_am654_enable_pll(struct serdes_am654 *phy) 24771e2f5c5SKishon Vijay Abraham I { 24871e2f5c5SKishon Vijay Abraham I int ret; 24971e2f5c5SKishon Vijay Abraham I u32 val; 25071e2f5c5SKishon Vijay Abraham I 251c3e60e5aSSekhar Nori ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_ENABLE_STATE); 25271e2f5c5SKishon Vijay Abraham I if (ret) 25371e2f5c5SKishon Vijay Abraham I return ret; 25471e2f5c5SKishon Vijay Abraham I 255c3e60e5aSSekhar Nori return regmap_field_read_poll_timeout(phy->fields[PLL_OK], val, val, 256c3e60e5aSSekhar Nori 1000, PLL_LOCK_TIME); 25771e2f5c5SKishon Vijay Abraham I } 25871e2f5c5SKishon Vijay Abraham I 25971e2f5c5SKishon Vijay Abraham I static void serdes_am654_disable_pll(struct serdes_am654 *phy) 26071e2f5c5SKishon Vijay Abraham I { 26171e2f5c5SKishon Vijay Abraham I struct device *dev = phy->dev; 26271e2f5c5SKishon Vijay Abraham I int ret; 26371e2f5c5SKishon Vijay Abraham I 264c3e60e5aSSekhar Nori ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_DISABLE_STATE); 26571e2f5c5SKishon Vijay Abraham I if (ret) 26671e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to disable PLL\n"); 26771e2f5c5SKishon Vijay Abraham I } 26871e2f5c5SKishon Vijay Abraham I 26971e2f5c5SKishon Vijay Abraham I static int serdes_am654_enable_txrx(struct serdes_am654 *phy) 27071e2f5c5SKishon Vijay Abraham I { 271b494bbb6SSekhar Nori int ret = 0; 27271e2f5c5SKishon Vijay Abraham I 27371e2f5c5SKishon Vijay Abraham I /* Enable TX */ 274b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_ENABLE_STATE); 27571e2f5c5SKishon Vijay Abraham I 27671e2f5c5SKishon Vijay Abraham I /* Enable RX */ 277b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_ENABLE_STATE); 278b494bbb6SSekhar Nori 27971e2f5c5SKishon Vijay Abraham I if (ret) 280b494bbb6SSekhar Nori return -EIO; 28171e2f5c5SKishon Vijay Abraham I 28271e2f5c5SKishon Vijay Abraham I return 0; 28371e2f5c5SKishon Vijay Abraham I } 28471e2f5c5SKishon Vijay Abraham I 28571e2f5c5SKishon Vijay Abraham I static int serdes_am654_disable_txrx(struct serdes_am654 *phy) 28671e2f5c5SKishon Vijay Abraham I { 287b494bbb6SSekhar Nori int ret = 0; 28871e2f5c5SKishon Vijay Abraham I 28971e2f5c5SKishon Vijay Abraham I /* Disable TX */ 290b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_DISABLE_STATE); 29171e2f5c5SKishon Vijay Abraham I 29271e2f5c5SKishon Vijay Abraham I /* Disable RX */ 293b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_DISABLE_STATE); 294b494bbb6SSekhar Nori 29571e2f5c5SKishon Vijay Abraham I if (ret) 296b494bbb6SSekhar Nori return -EIO; 29771e2f5c5SKishon Vijay Abraham I 29871e2f5c5SKishon Vijay Abraham I return 0; 29971e2f5c5SKishon Vijay Abraham I } 30071e2f5c5SKishon Vijay Abraham I 30171e2f5c5SKishon Vijay Abraham I static int serdes_am654_power_on(struct phy *x) 30271e2f5c5SKishon Vijay Abraham I { 30371e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 30471e2f5c5SKishon Vijay Abraham I struct device *dev = phy->dev; 30571e2f5c5SKishon Vijay Abraham I int ret; 30671e2f5c5SKishon Vijay Abraham I u32 val; 30771e2f5c5SKishon Vijay Abraham I 30871e2f5c5SKishon Vijay Abraham I ret = serdes_am654_enable_pll(phy); 30971e2f5c5SKishon Vijay Abraham I if (ret) { 31071e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to enable PLL\n"); 31171e2f5c5SKishon Vijay Abraham I return ret; 31271e2f5c5SKishon Vijay Abraham I } 31371e2f5c5SKishon Vijay Abraham I 31471e2f5c5SKishon Vijay Abraham I ret = serdes_am654_enable_txrx(phy); 31571e2f5c5SKishon Vijay Abraham I if (ret) { 31671e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to enable TX RX\n"); 31771e2f5c5SKishon Vijay Abraham I return ret; 31871e2f5c5SKishon Vijay Abraham I } 31971e2f5c5SKishon Vijay Abraham I 320c3e60e5aSSekhar Nori return regmap_field_read_poll_timeout(phy->fields[CMU_OK_I_0], val, 321c3e60e5aSSekhar Nori val, SLEEP_TIME, PLL_LOCK_TIME); 32271e2f5c5SKishon Vijay Abraham I } 32371e2f5c5SKishon Vijay Abraham I 32471e2f5c5SKishon Vijay Abraham I static int serdes_am654_power_off(struct phy *x) 32571e2f5c5SKishon Vijay Abraham I { 32671e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 32771e2f5c5SKishon Vijay Abraham I 32871e2f5c5SKishon Vijay Abraham I serdes_am654_disable_txrx(phy); 32971e2f5c5SKishon Vijay Abraham I serdes_am654_disable_pll(phy); 33071e2f5c5SKishon Vijay Abraham I 33171e2f5c5SKishon Vijay Abraham I return 0; 33271e2f5c5SKishon Vijay Abraham I } 33371e2f5c5SKishon Vijay Abraham I 334257d0be3SRoger Quadros #define SERDES_AM654_CFG(offset, a, b, val) \ 335257d0be3SRoger Quadros regmap_update_bits(phy->regmap, (offset),\ 336257d0be3SRoger Quadros GENMASK((a), (b)), (val) << (b)) 337257d0be3SRoger Quadros 338257d0be3SRoger Quadros static int serdes_am654_usb3_init(struct serdes_am654 *phy) 33971e2f5c5SKishon Vijay Abraham I { 340257d0be3SRoger Quadros SERDES_AM654_CFG(0x0000, 31, 24, 0x17); 341257d0be3SRoger Quadros SERDES_AM654_CFG(0x0004, 15, 8, 0x02); 342257d0be3SRoger Quadros SERDES_AM654_CFG(0x0004, 7, 0, 0x0e); 343257d0be3SRoger Quadros SERDES_AM654_CFG(0x0008, 23, 16, 0x2e); 344257d0be3SRoger Quadros SERDES_AM654_CFG(0x0008, 31, 24, 0x2e); 345257d0be3SRoger Quadros SERDES_AM654_CFG(0x0060, 7, 0, 0x4b); 346257d0be3SRoger Quadros SERDES_AM654_CFG(0x0060, 15, 8, 0x98); 347257d0be3SRoger Quadros SERDES_AM654_CFG(0x0060, 23, 16, 0x60); 348257d0be3SRoger Quadros SERDES_AM654_CFG(0x00d0, 31, 24, 0x45); 349257d0be3SRoger Quadros SERDES_AM654_CFG(0x00e8, 15, 8, 0x0e); 350257d0be3SRoger Quadros SERDES_AM654_CFG(0x0220, 7, 0, 0x34); 351257d0be3SRoger Quadros SERDES_AM654_CFG(0x0220, 15, 8, 0x34); 352257d0be3SRoger Quadros SERDES_AM654_CFG(0x0220, 31, 24, 0x37); 353257d0be3SRoger Quadros SERDES_AM654_CFG(0x0224, 7, 0, 0x37); 354257d0be3SRoger Quadros SERDES_AM654_CFG(0x0224, 15, 8, 0x37); 355257d0be3SRoger Quadros SERDES_AM654_CFG(0x0228, 23, 16, 0x37); 356257d0be3SRoger Quadros SERDES_AM654_CFG(0x0228, 31, 24, 0x37); 357257d0be3SRoger Quadros SERDES_AM654_CFG(0x022c, 7, 0, 0x37); 358257d0be3SRoger Quadros SERDES_AM654_CFG(0x022c, 15, 8, 0x37); 359257d0be3SRoger Quadros SERDES_AM654_CFG(0x0230, 15, 8, 0x2a); 360257d0be3SRoger Quadros SERDES_AM654_CFG(0x0230, 23, 16, 0x2a); 361257d0be3SRoger Quadros SERDES_AM654_CFG(0x0240, 23, 16, 0x10); 362257d0be3SRoger Quadros SERDES_AM654_CFG(0x0240, 31, 24, 0x34); 363257d0be3SRoger Quadros SERDES_AM654_CFG(0x0244, 7, 0, 0x40); 364257d0be3SRoger Quadros SERDES_AM654_CFG(0x0244, 23, 16, 0x34); 365257d0be3SRoger Quadros SERDES_AM654_CFG(0x0248, 15, 8, 0x0d); 366257d0be3SRoger Quadros SERDES_AM654_CFG(0x0258, 15, 8, 0x16); 367257d0be3SRoger Quadros SERDES_AM654_CFG(0x0258, 23, 16, 0x84); 368257d0be3SRoger Quadros SERDES_AM654_CFG(0x0258, 31, 24, 0xf2); 369257d0be3SRoger Quadros SERDES_AM654_CFG(0x025c, 7, 0, 0x21); 370257d0be3SRoger Quadros SERDES_AM654_CFG(0x0260, 7, 0, 0x27); 371257d0be3SRoger Quadros SERDES_AM654_CFG(0x0260, 15, 8, 0x04); 372257d0be3SRoger Quadros SERDES_AM654_CFG(0x0268, 15, 8, 0x04); 373257d0be3SRoger Quadros SERDES_AM654_CFG(0x0288, 15, 8, 0x2c); 374257d0be3SRoger Quadros SERDES_AM654_CFG(0x0330, 31, 24, 0xa0); 375257d0be3SRoger Quadros SERDES_AM654_CFG(0x0338, 23, 16, 0x03); 376257d0be3SRoger Quadros SERDES_AM654_CFG(0x0338, 31, 24, 0x00); 377257d0be3SRoger Quadros SERDES_AM654_CFG(0x033c, 7, 0, 0x00); 378257d0be3SRoger Quadros SERDES_AM654_CFG(0x0344, 31, 24, 0x18); 379257d0be3SRoger Quadros SERDES_AM654_CFG(0x034c, 7, 0, 0x18); 380257d0be3SRoger Quadros SERDES_AM654_CFG(0x039c, 23, 16, 0x3b); 381257d0be3SRoger Quadros SERDES_AM654_CFG(0x0a04, 7, 0, 0x03); 382257d0be3SRoger Quadros SERDES_AM654_CFG(0x0a14, 31, 24, 0x3c); 383257d0be3SRoger Quadros SERDES_AM654_CFG(0x0a18, 15, 8, 0x3c); 384257d0be3SRoger Quadros SERDES_AM654_CFG(0x0a38, 7, 0, 0x3e); 385257d0be3SRoger Quadros SERDES_AM654_CFG(0x0a38, 15, 8, 0x3e); 386257d0be3SRoger Quadros SERDES_AM654_CFG(0x0ae0, 7, 0, 0x07); 387257d0be3SRoger Quadros SERDES_AM654_CFG(0x0b6c, 23, 16, 0xcd); 388257d0be3SRoger Quadros SERDES_AM654_CFG(0x0b6c, 31, 24, 0x04); 389257d0be3SRoger Quadros SERDES_AM654_CFG(0x0b98, 23, 16, 0x03); 390257d0be3SRoger Quadros SERDES_AM654_CFG(0x1400, 7, 0, 0x3f); 391257d0be3SRoger Quadros SERDES_AM654_CFG(0x1404, 23, 16, 0x6f); 392257d0be3SRoger Quadros SERDES_AM654_CFG(0x1404, 31, 24, 0x6f); 393257d0be3SRoger Quadros SERDES_AM654_CFG(0x140c, 7, 0, 0x6f); 394257d0be3SRoger Quadros SERDES_AM654_CFG(0x140c, 15, 8, 0x6f); 395257d0be3SRoger Quadros SERDES_AM654_CFG(0x1410, 15, 8, 0x27); 396257d0be3SRoger Quadros SERDES_AM654_CFG(0x1414, 7, 0, 0x0c); 397257d0be3SRoger Quadros SERDES_AM654_CFG(0x1414, 23, 16, 0x07); 398257d0be3SRoger Quadros SERDES_AM654_CFG(0x1418, 23, 16, 0x40); 399257d0be3SRoger Quadros SERDES_AM654_CFG(0x141c, 7, 0, 0x00); 400257d0be3SRoger Quadros SERDES_AM654_CFG(0x141c, 15, 8, 0x1f); 401257d0be3SRoger Quadros SERDES_AM654_CFG(0x1428, 31, 24, 0x08); 402257d0be3SRoger Quadros SERDES_AM654_CFG(0x1434, 31, 24, 0x00); 403257d0be3SRoger Quadros SERDES_AM654_CFG(0x1444, 7, 0, 0x94); 404257d0be3SRoger Quadros SERDES_AM654_CFG(0x1460, 31, 24, 0x7f); 405257d0be3SRoger Quadros SERDES_AM654_CFG(0x1464, 7, 0, 0x43); 406257d0be3SRoger Quadros SERDES_AM654_CFG(0x1464, 23, 16, 0x6f); 407257d0be3SRoger Quadros SERDES_AM654_CFG(0x1464, 31, 24, 0x43); 408257d0be3SRoger Quadros SERDES_AM654_CFG(0x1484, 23, 16, 0x8f); 409257d0be3SRoger Quadros SERDES_AM654_CFG(0x1498, 7, 0, 0x4f); 410257d0be3SRoger Quadros SERDES_AM654_CFG(0x1498, 23, 16, 0x4f); 411257d0be3SRoger Quadros SERDES_AM654_CFG(0x007c, 31, 24, 0x0d); 412257d0be3SRoger Quadros SERDES_AM654_CFG(0x0b90, 15, 8, 0x0f); 413257d0be3SRoger Quadros 414257d0be3SRoger Quadros return 0; 415257d0be3SRoger Quadros } 416257d0be3SRoger Quadros 417257d0be3SRoger Quadros static int serdes_am654_pcie_init(struct serdes_am654 *phy) 418257d0be3SRoger Quadros { 419b494bbb6SSekhar Nori int ret = 0; 42071e2f5c5SKishon Vijay Abraham I 421f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[CMU_PLL_CTRL], 0x2); 422f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_VBIAS_VREG], 0x98); 423f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_BIAS_VREG], 0x98); 424f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_CM_SR], 0x45); 425f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_SSC_GEN_Z_O_20_13], 0xe); 426f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[LANE_PLL_CTRL_RXEQ_RXIDLE], 0x5); 427f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_LN_AGC_THSEL_VREGH], 0x83); 428f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_LN_GEN3_AGC_SD_THSEL], 0x83); 429f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_LN_RX_SELR_GEN3], 0x81); 430f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[AHB_PMA_LN_TX_DRV], 0x3b); 431f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[P2S_RBUF_PTR_DIFF], 0x3); 432b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[CONFIG_VERSION], VERSION_VAL); 433f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_MS_INIT_CTRL_7_0], 0xf); 434f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_HS_INIT_CAL_7_0], 0x4f); 435f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_MS_RECAL_CTRL_7_0], 0xf); 436f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RECAL_CTRL_7_0], 0x4f); 437f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_ATT_CONFIG], 0x7); 438f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_EBSTADAPT_WIN_LEN], 0x7f); 439f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_3_4], 0xf); 440f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_14_15_16], 0x9a); 441f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_DLEV_ERR_THRESH], 0x32); 442f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_25], 0x80); 443f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O], 0xf); 444f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RCHANGE_CTRL_7_0], 0x4f); 445b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[CMU_MASTER_CDN], 0x1); 446f78c40aaSSekhar Nori ret |= regmap_field_write(phy->fields[L1_MASTER_CDN], 0x2); 44771e2f5c5SKishon Vijay Abraham I 44871e2f5c5SKishon Vijay Abraham I if (ret) 449b494bbb6SSekhar Nori return -EIO; 45071e2f5c5SKishon Vijay Abraham I 45171e2f5c5SKishon Vijay Abraham I return 0; 45271e2f5c5SKishon Vijay Abraham I } 45371e2f5c5SKishon Vijay Abraham I 454257d0be3SRoger Quadros static int serdes_am654_init(struct phy *x) 455257d0be3SRoger Quadros { 456257d0be3SRoger Quadros struct serdes_am654 *phy = phy_get_drvdata(x); 457257d0be3SRoger Quadros 458257d0be3SRoger Quadros switch (phy->type) { 459257d0be3SRoger Quadros case PHY_TYPE_PCIE: 460257d0be3SRoger Quadros return serdes_am654_pcie_init(phy); 461257d0be3SRoger Quadros case PHY_TYPE_USB3: 462257d0be3SRoger Quadros return serdes_am654_usb3_init(phy); 463257d0be3SRoger Quadros default: 464257d0be3SRoger Quadros return -EINVAL; 465257d0be3SRoger Quadros } 466257d0be3SRoger Quadros } 467257d0be3SRoger Quadros 46871e2f5c5SKishon Vijay Abraham I static int serdes_am654_reset(struct phy *x) 46971e2f5c5SKishon Vijay Abraham I { 47071e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 471b494bbb6SSekhar Nori int ret = 0; 47271e2f5c5SKishon Vijay Abraham I 473257d0be3SRoger Quadros serdes_am654_disable_pll(phy); 474257d0be3SRoger Quadros serdes_am654_disable_txrx(phy); 475257d0be3SRoger Quadros 476b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[POR_EN], 0x1); 47771e2f5c5SKishon Vijay Abraham I 47871e2f5c5SKishon Vijay Abraham I mdelay(1); 47971e2f5c5SKishon Vijay Abraham I 480b494bbb6SSekhar Nori ret |= regmap_field_write(phy->fields[POR_EN], 0x0); 481b494bbb6SSekhar Nori 48271e2f5c5SKishon Vijay Abraham I if (ret) 483b494bbb6SSekhar Nori return -EIO; 48471e2f5c5SKishon Vijay Abraham I 48571e2f5c5SKishon Vijay Abraham I return 0; 48671e2f5c5SKishon Vijay Abraham I } 48771e2f5c5SKishon Vijay Abraham I 48871e2f5c5SKishon Vijay Abraham I static void serdes_am654_release(struct phy *x) 48971e2f5c5SKishon Vijay Abraham I { 49071e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 49171e2f5c5SKishon Vijay Abraham I 49271e2f5c5SKishon Vijay Abraham I phy->type = PHY_NONE; 49371e2f5c5SKishon Vijay Abraham I phy->busy = false; 49471e2f5c5SKishon Vijay Abraham I mux_control_deselect(phy->control); 49571e2f5c5SKishon Vijay Abraham I } 49671e2f5c5SKishon Vijay Abraham I 4971853bc0aSYueHaibing static struct phy *serdes_am654_xlate(struct device *dev, 4981853bc0aSYueHaibing struct of_phandle_args *args) 49971e2f5c5SKishon Vijay Abraham I { 50071e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy; 50171e2f5c5SKishon Vijay Abraham I struct phy *phy; 50271e2f5c5SKishon Vijay Abraham I int ret; 50371e2f5c5SKishon Vijay Abraham I 50471e2f5c5SKishon Vijay Abraham I phy = of_phy_simple_xlate(dev, args); 50571e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy)) 50671e2f5c5SKishon Vijay Abraham I return phy; 50771e2f5c5SKishon Vijay Abraham I 50871e2f5c5SKishon Vijay Abraham I am654_phy = phy_get_drvdata(phy); 50971e2f5c5SKishon Vijay Abraham I if (am654_phy->busy) 51071e2f5c5SKishon Vijay Abraham I return ERR_PTR(-EBUSY); 51171e2f5c5SKishon Vijay Abraham I 51271e2f5c5SKishon Vijay Abraham I ret = mux_control_select(am654_phy->control, args->args[1]); 51371e2f5c5SKishon Vijay Abraham I if (ret) { 51471e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to select SERDES Lane Function\n"); 51571e2f5c5SKishon Vijay Abraham I return ERR_PTR(ret); 51671e2f5c5SKishon Vijay Abraham I } 51771e2f5c5SKishon Vijay Abraham I 51871e2f5c5SKishon Vijay Abraham I am654_phy->busy = true; 51971e2f5c5SKishon Vijay Abraham I am654_phy->type = args->args[0]; 52071e2f5c5SKishon Vijay Abraham I 52171e2f5c5SKishon Vijay Abraham I return phy; 52271e2f5c5SKishon Vijay Abraham I } 52371e2f5c5SKishon Vijay Abraham I 52471e2f5c5SKishon Vijay Abraham I static const struct phy_ops ops = { 52571e2f5c5SKishon Vijay Abraham I .reset = serdes_am654_reset, 52671e2f5c5SKishon Vijay Abraham I .init = serdes_am654_init, 52771e2f5c5SKishon Vijay Abraham I .power_on = serdes_am654_power_on, 52871e2f5c5SKishon Vijay Abraham I .power_off = serdes_am654_power_off, 52971e2f5c5SKishon Vijay Abraham I .release = serdes_am654_release, 53071e2f5c5SKishon Vijay Abraham I .owner = THIS_MODULE, 53171e2f5c5SKishon Vijay Abraham I }; 53271e2f5c5SKishon Vijay Abraham I 5337e7b8ca6SRoger Quadros #define SERDES_NUM_MUX_COMBINATIONS 16 5347e7b8ca6SRoger Quadros 5357e7b8ca6SRoger Quadros #define LICLK 0 5367e7b8ca6SRoger Quadros #define EXT_REFCLK 1 5377e7b8ca6SRoger Quadros #define RICLK 2 5387e7b8ca6SRoger Quadros 5397e7b8ca6SRoger Quadros static const int 5407e7b8ca6SRoger Quadros serdes_am654_mux_table[SERDES_NUM_MUX_COMBINATIONS][SERDES_NUM_CLOCKS] = { 5417e7b8ca6SRoger Quadros /* 5427e7b8ca6SRoger Quadros * Each combination maps to one of 5437e7b8ca6SRoger Quadros * "Figure 12-1986. SerDes Reference Clock Distribution" 5447e7b8ca6SRoger Quadros * in TRM. 5457e7b8ca6SRoger Quadros */ 5467e7b8ca6SRoger Quadros /* Parent of CMU refclk, Left output, Right output 5477e7b8ca6SRoger Quadros * either of EXT_REFCLK, LICLK, RICLK 5487e7b8ca6SRoger Quadros */ 5497e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0000 */ 5507e7b8ca6SRoger Quadros { RICLK, EXT_REFCLK, EXT_REFCLK }, /* 0001 */ 5517e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, LICLK }, /* 0010 */ 5527e7b8ca6SRoger Quadros { RICLK, RICLK, EXT_REFCLK }, /* 0011 */ 5537e7b8ca6SRoger Quadros { LICLK, EXT_REFCLK, EXT_REFCLK }, /* 0100 */ 5547e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0101 */ 5557e7b8ca6SRoger Quadros { LICLK, RICLK, LICLK }, /* 0110 */ 5567e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, LICLK }, /* 0111 */ 5577e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1000 */ 5587e7b8ca6SRoger Quadros { RICLK, EXT_REFCLK, LICLK }, /* 1001 */ 5597e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1010 */ 5607e7b8ca6SRoger Quadros { RICLK, RICLK, EXT_REFCLK }, /* 1011 */ 5617e7b8ca6SRoger Quadros { LICLK, EXT_REFCLK, LICLK }, /* 1100 */ 5627e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1101 */ 5637e7b8ca6SRoger Quadros { LICLK, RICLK, EXT_REFCLK }, /* 1110 */ 5647e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1111 */ 5657e7b8ca6SRoger Quadros }; 5667e7b8ca6SRoger Quadros 56771e2f5c5SKishon Vijay Abraham I static u8 serdes_am654_clk_mux_get_parent(struct clk_hw *hw) 56871e2f5c5SKishon Vijay Abraham I { 56971e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); 57071e2f5c5SKishon Vijay Abraham I struct regmap *regmap = mux->regmap; 57171e2f5c5SKishon Vijay Abraham I unsigned int reg = mux->reg; 57271e2f5c5SKishon Vijay Abraham I unsigned int val; 57371e2f5c5SKishon Vijay Abraham I 57471e2f5c5SKishon Vijay Abraham I regmap_read(regmap, reg, &val); 5757e7b8ca6SRoger Quadros val &= AM654_SERDES_CTRL_CLKSEL_MASK; 5767e7b8ca6SRoger Quadros val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; 57771e2f5c5SKishon Vijay Abraham I 5787e7b8ca6SRoger Quadros return serdes_am654_mux_table[val][mux->clk_id]; 57971e2f5c5SKishon Vijay Abraham I } 58071e2f5c5SKishon Vijay Abraham I 58171e2f5c5SKishon Vijay Abraham I static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index) 58271e2f5c5SKishon Vijay Abraham I { 58371e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); 58471e2f5c5SKishon Vijay Abraham I struct regmap *regmap = mux->regmap; 585bd0e79f1SStephen Boyd const char *name = clk_hw_get_name(hw); 58671e2f5c5SKishon Vijay Abraham I unsigned int reg = mux->reg; 5877e7b8ca6SRoger Quadros int clk_id = mux->clk_id; 5887e7b8ca6SRoger Quadros int parents[SERDES_NUM_CLOCKS]; 5897e7b8ca6SRoger Quadros const int *p; 5907e7b8ca6SRoger Quadros u32 val; 5917e7b8ca6SRoger Quadros int found, i; 59271e2f5c5SKishon Vijay Abraham I int ret; 59371e2f5c5SKishon Vijay Abraham I 5947e7b8ca6SRoger Quadros /* get existing setting */ 5957e7b8ca6SRoger Quadros regmap_read(regmap, reg, &val); 5967e7b8ca6SRoger Quadros val &= AM654_SERDES_CTRL_CLKSEL_MASK; 5977e7b8ca6SRoger Quadros val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; 59871e2f5c5SKishon Vijay Abraham I 5997e7b8ca6SRoger Quadros for (i = 0; i < SERDES_NUM_CLOCKS; i++) 6007e7b8ca6SRoger Quadros parents[i] = serdes_am654_mux_table[val][i]; 6017e7b8ca6SRoger Quadros 6027e7b8ca6SRoger Quadros /* change parent of this clock. others left intact */ 6037e7b8ca6SRoger Quadros parents[clk_id] = index; 6047e7b8ca6SRoger Quadros 6057e7b8ca6SRoger Quadros /* Find the match */ 6067e7b8ca6SRoger Quadros for (val = 0; val < SERDES_NUM_MUX_COMBINATIONS; val++) { 6077e7b8ca6SRoger Quadros p = serdes_am654_mux_table[val]; 6087e7b8ca6SRoger Quadros found = 1; 6097e7b8ca6SRoger Quadros for (i = 0; i < SERDES_NUM_CLOCKS; i++) { 6107e7b8ca6SRoger Quadros if (parents[i] != p[i]) { 6117e7b8ca6SRoger Quadros found = 0; 6127e7b8ca6SRoger Quadros break; 6137e7b8ca6SRoger Quadros } 6147e7b8ca6SRoger Quadros } 6157e7b8ca6SRoger Quadros 6167e7b8ca6SRoger Quadros if (found) 6177e7b8ca6SRoger Quadros break; 6187e7b8ca6SRoger Quadros } 6197e7b8ca6SRoger Quadros 6207e7b8ca6SRoger Quadros if (!found) { 6217e7b8ca6SRoger Quadros /* 6227e7b8ca6SRoger Quadros * This can never happen, unless we missed 6237e7b8ca6SRoger Quadros * a valid combination in serdes_am654_mux_table. 6247e7b8ca6SRoger Quadros */ 625bd0e79f1SStephen Boyd WARN(1, "Failed to find the parent of %s clock\n", name); 62671e2f5c5SKishon Vijay Abraham I return -EINVAL; 6277e7b8ca6SRoger Quadros } 62871e2f5c5SKishon Vijay Abraham I 6297e7b8ca6SRoger Quadros val <<= AM654_SERDES_CTRL_CLKSEL_SHIFT; 6307e7b8ca6SRoger Quadros ret = regmap_update_bits(regmap, reg, AM654_SERDES_CTRL_CLKSEL_MASK, 6317e7b8ca6SRoger Quadros val); 63271e2f5c5SKishon Vijay Abraham I 63371e2f5c5SKishon Vijay Abraham I return ret; 63471e2f5c5SKishon Vijay Abraham I } 63571e2f5c5SKishon Vijay Abraham I 63671e2f5c5SKishon Vijay Abraham I static const struct clk_ops serdes_am654_clk_mux_ops = { 63771e2f5c5SKishon Vijay Abraham I .set_parent = serdes_am654_clk_mux_set_parent, 63871e2f5c5SKishon Vijay Abraham I .get_parent = serdes_am654_clk_mux_get_parent, 63971e2f5c5SKishon Vijay Abraham I }; 64071e2f5c5SKishon Vijay Abraham I 64171e2f5c5SKishon Vijay Abraham I static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, 64271e2f5c5SKishon Vijay Abraham I const char *clock_name, int clock_num) 64371e2f5c5SKishon Vijay Abraham I { 64471e2f5c5SKishon Vijay Abraham I struct device_node *node = am654_phy->of_node; 64571e2f5c5SKishon Vijay Abraham I struct device *dev = am654_phy->dev; 64671e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux; 64771e2f5c5SKishon Vijay Abraham I struct device_node *regmap_node; 64871e2f5c5SKishon Vijay Abraham I const char **parent_names; 64971e2f5c5SKishon Vijay Abraham I struct clk_init_data *init; 65071e2f5c5SKishon Vijay Abraham I unsigned int num_parents; 65171e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 65271e2f5c5SKishon Vijay Abraham I const __be32 *addr; 65371e2f5c5SKishon Vijay Abraham I unsigned int reg; 65471e2f5c5SKishon Vijay Abraham I struct clk *clk; 6553e644828SWen Yang int ret = 0; 65671e2f5c5SKishon Vijay Abraham I 65771e2f5c5SKishon Vijay Abraham I mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 65871e2f5c5SKishon Vijay Abraham I if (!mux) 65971e2f5c5SKishon Vijay Abraham I return -ENOMEM; 66071e2f5c5SKishon Vijay Abraham I 66171e2f5c5SKishon Vijay Abraham I init = &mux->clk_data; 66271e2f5c5SKishon Vijay Abraham I 66371e2f5c5SKishon Vijay Abraham I regmap_node = of_parse_phandle(node, "ti,serdes-clk", 0); 66471e2f5c5SKishon Vijay Abraham I if (!regmap_node) { 66571e2f5c5SKishon Vijay Abraham I dev_err(dev, "Fail to get serdes-clk node\n"); 6663e644828SWen Yang ret = -ENODEV; 6673e644828SWen Yang goto out_put_node; 66871e2f5c5SKishon Vijay Abraham I } 66971e2f5c5SKishon Vijay Abraham I 67071e2f5c5SKishon Vijay Abraham I regmap = syscon_node_to_regmap(regmap_node->parent); 67171e2f5c5SKishon Vijay Abraham I if (IS_ERR(regmap)) { 67271e2f5c5SKishon Vijay Abraham I dev_err(dev, "Fail to get Syscon regmap\n"); 6733e644828SWen Yang ret = PTR_ERR(regmap); 6743e644828SWen Yang goto out_put_node; 67571e2f5c5SKishon Vijay Abraham I } 67671e2f5c5SKishon Vijay Abraham I 67771e2f5c5SKishon Vijay Abraham I num_parents = of_clk_get_parent_count(node); 67871e2f5c5SKishon Vijay Abraham I if (num_parents < 2) { 67971e2f5c5SKishon Vijay Abraham I dev_err(dev, "SERDES clock must have parents\n"); 6803e644828SWen Yang ret = -EINVAL; 6813e644828SWen Yang goto out_put_node; 68271e2f5c5SKishon Vijay Abraham I } 68371e2f5c5SKishon Vijay Abraham I 68471e2f5c5SKishon Vijay Abraham I parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), 68571e2f5c5SKishon Vijay Abraham I GFP_KERNEL); 6863e644828SWen Yang if (!parent_names) { 6873e644828SWen Yang ret = -ENOMEM; 6883e644828SWen Yang goto out_put_node; 6893e644828SWen Yang } 69071e2f5c5SKishon Vijay Abraham I 69171e2f5c5SKishon Vijay Abraham I of_clk_parent_fill(node, parent_names, num_parents); 69271e2f5c5SKishon Vijay Abraham I 69371e2f5c5SKishon Vijay Abraham I addr = of_get_address(regmap_node, 0, NULL, NULL); 6943e644828SWen Yang if (!addr) { 6953e644828SWen Yang ret = -EINVAL; 6963e644828SWen Yang goto out_put_node; 6973e644828SWen Yang } 69871e2f5c5SKishon Vijay Abraham I 69971e2f5c5SKishon Vijay Abraham I reg = be32_to_cpu(*addr); 70071e2f5c5SKishon Vijay Abraham I 70171e2f5c5SKishon Vijay Abraham I init->ops = &serdes_am654_clk_mux_ops; 70271e2f5c5SKishon Vijay Abraham I init->flags = CLK_SET_RATE_NO_REPARENT; 70371e2f5c5SKishon Vijay Abraham I init->parent_names = parent_names; 70471e2f5c5SKishon Vijay Abraham I init->num_parents = num_parents; 70571e2f5c5SKishon Vijay Abraham I init->name = clock_name; 70671e2f5c5SKishon Vijay Abraham I 70771e2f5c5SKishon Vijay Abraham I mux->regmap = regmap; 70871e2f5c5SKishon Vijay Abraham I mux->reg = reg; 7097e7b8ca6SRoger Quadros mux->clk_id = clock_num; 71071e2f5c5SKishon Vijay Abraham I mux->hw.init = init; 71171e2f5c5SKishon Vijay Abraham I 71271e2f5c5SKishon Vijay Abraham I clk = devm_clk_register(dev, &mux->hw); 7133e644828SWen Yang if (IS_ERR(clk)) { 7143e644828SWen Yang ret = PTR_ERR(clk); 7153e644828SWen Yang goto out_put_node; 7163e644828SWen Yang } 71771e2f5c5SKishon Vijay Abraham I 71871e2f5c5SKishon Vijay Abraham I am654_phy->clks[clock_num] = clk; 71971e2f5c5SKishon Vijay Abraham I 7203e644828SWen Yang out_put_node: 7213e644828SWen Yang of_node_put(regmap_node); 7223e644828SWen Yang return ret; 72371e2f5c5SKishon Vijay Abraham I } 72471e2f5c5SKishon Vijay Abraham I 72571e2f5c5SKishon Vijay Abraham I static const struct of_device_id serdes_am654_id_table[] = { 72671e2f5c5SKishon Vijay Abraham I { 72771e2f5c5SKishon Vijay Abraham I .compatible = "ti,phy-am654-serdes", 72871e2f5c5SKishon Vijay Abraham I }, 72971e2f5c5SKishon Vijay Abraham I {} 73071e2f5c5SKishon Vijay Abraham I }; 73171e2f5c5SKishon Vijay Abraham I MODULE_DEVICE_TABLE(of, serdes_am654_id_table); 73271e2f5c5SKishon Vijay Abraham I 73371e2f5c5SKishon Vijay Abraham I static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy) 73471e2f5c5SKishon Vijay Abraham I { 73571e2f5c5SKishon Vijay Abraham I struct regmap *regmap = am654_phy->regmap; 73671e2f5c5SKishon Vijay Abraham I struct device *dev = am654_phy->dev; 737c3e60e5aSSekhar Nori int i; 73871e2f5c5SKishon Vijay Abraham I 739c3e60e5aSSekhar Nori for (i = 0; i < MAX_FIELDS; i++) { 740c3e60e5aSSekhar Nori am654_phy->fields[i] = devm_regmap_field_alloc(dev, 741c3e60e5aSSekhar Nori regmap, 742c3e60e5aSSekhar Nori serdes_am654_reg_fields[i]); 743c3e60e5aSSekhar Nori if (IS_ERR(am654_phy->fields[i])) { 744c3e60e5aSSekhar Nori dev_err(dev, "Unable to allocate regmap field %d\n", i); 745c3e60e5aSSekhar Nori return PTR_ERR(am654_phy->fields[i]); 74671e2f5c5SKishon Vijay Abraham I } 74771e2f5c5SKishon Vijay Abraham I } 74871e2f5c5SKishon Vijay Abraham I 74971e2f5c5SKishon Vijay Abraham I return 0; 75071e2f5c5SKishon Vijay Abraham I } 75171e2f5c5SKishon Vijay Abraham I 75271e2f5c5SKishon Vijay Abraham I static int serdes_am654_probe(struct platform_device *pdev) 75371e2f5c5SKishon Vijay Abraham I { 75471e2f5c5SKishon Vijay Abraham I struct phy_provider *phy_provider; 75571e2f5c5SKishon Vijay Abraham I struct device *dev = &pdev->dev; 75671e2f5c5SKishon Vijay Abraham I struct device_node *node = dev->of_node; 75771e2f5c5SKishon Vijay Abraham I struct clk_onecell_data *clk_data; 75871e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy; 75971e2f5c5SKishon Vijay Abraham I struct mux_control *control; 76071e2f5c5SKishon Vijay Abraham I const char *clock_name; 76171e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 76271e2f5c5SKishon Vijay Abraham I void __iomem *base; 76371e2f5c5SKishon Vijay Abraham I struct phy *phy; 76471e2f5c5SKishon Vijay Abraham I int ret; 76571e2f5c5SKishon Vijay Abraham I int i; 76671e2f5c5SKishon Vijay Abraham I 76771e2f5c5SKishon Vijay Abraham I am654_phy = devm_kzalloc(dev, sizeof(*am654_phy), GFP_KERNEL); 76871e2f5c5SKishon Vijay Abraham I if (!am654_phy) 76971e2f5c5SKishon Vijay Abraham I return -ENOMEM; 77071e2f5c5SKishon Vijay Abraham I 77171e2f5c5SKishon Vijay Abraham I base = devm_platform_ioremap_resource(pdev, 0); 77271e2f5c5SKishon Vijay Abraham I if (IS_ERR(base)) 77371e2f5c5SKishon Vijay Abraham I return PTR_ERR(base); 77471e2f5c5SKishon Vijay Abraham I 77571e2f5c5SKishon Vijay Abraham I regmap = devm_regmap_init_mmio(dev, base, &serdes_am654_regmap_config); 77671e2f5c5SKishon Vijay Abraham I if (IS_ERR(regmap)) { 77771e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize regmap\n"); 77871e2f5c5SKishon Vijay Abraham I return PTR_ERR(regmap); 77971e2f5c5SKishon Vijay Abraham I } 78071e2f5c5SKishon Vijay Abraham I 78171e2f5c5SKishon Vijay Abraham I control = devm_mux_control_get(dev, NULL); 78271e2f5c5SKishon Vijay Abraham I if (IS_ERR(control)) 78371e2f5c5SKishon Vijay Abraham I return PTR_ERR(control); 78471e2f5c5SKishon Vijay Abraham I 78571e2f5c5SKishon Vijay Abraham I am654_phy->dev = dev; 78671e2f5c5SKishon Vijay Abraham I am654_phy->of_node = node; 78771e2f5c5SKishon Vijay Abraham I am654_phy->regmap = regmap; 78871e2f5c5SKishon Vijay Abraham I am654_phy->control = control; 78971e2f5c5SKishon Vijay Abraham I am654_phy->type = PHY_NONE; 79071e2f5c5SKishon Vijay Abraham I 79171e2f5c5SKishon Vijay Abraham I ret = serdes_am654_regfield_init(am654_phy); 79271e2f5c5SKishon Vijay Abraham I if (ret) { 79371e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize regfields\n"); 79471e2f5c5SKishon Vijay Abraham I return ret; 79571e2f5c5SKishon Vijay Abraham I } 79671e2f5c5SKishon Vijay Abraham I 79771e2f5c5SKishon Vijay Abraham I platform_set_drvdata(pdev, am654_phy); 79871e2f5c5SKishon Vijay Abraham I 79971e2f5c5SKishon Vijay Abraham I for (i = 0; i < SERDES_NUM_CLOCKS; i++) { 80071e2f5c5SKishon Vijay Abraham I ret = of_property_read_string_index(node, "clock-output-names", 80171e2f5c5SKishon Vijay Abraham I i, &clock_name); 80271e2f5c5SKishon Vijay Abraham I if (ret) { 80371e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to get clock name\n"); 80471e2f5c5SKishon Vijay Abraham I return ret; 80571e2f5c5SKishon Vijay Abraham I } 80671e2f5c5SKishon Vijay Abraham I 80771e2f5c5SKishon Vijay Abraham I ret = serdes_am654_clk_register(am654_phy, clock_name, i); 80871e2f5c5SKishon Vijay Abraham I if (ret) { 80971e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize clock %s\n", 81071e2f5c5SKishon Vijay Abraham I clock_name); 81171e2f5c5SKishon Vijay Abraham I return ret; 81271e2f5c5SKishon Vijay Abraham I } 81371e2f5c5SKishon Vijay Abraham I } 81471e2f5c5SKishon Vijay Abraham I 81571e2f5c5SKishon Vijay Abraham I clk_data = &am654_phy->clk_data; 81671e2f5c5SKishon Vijay Abraham I clk_data->clks = am654_phy->clks; 81771e2f5c5SKishon Vijay Abraham I clk_data->clk_num = SERDES_NUM_CLOCKS; 81871e2f5c5SKishon Vijay Abraham I ret = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); 81971e2f5c5SKishon Vijay Abraham I if (ret) 82071e2f5c5SKishon Vijay Abraham I return ret; 82171e2f5c5SKishon Vijay Abraham I 82271e2f5c5SKishon Vijay Abraham I pm_runtime_enable(dev); 82371e2f5c5SKishon Vijay Abraham I 82471e2f5c5SKishon Vijay Abraham I phy = devm_phy_create(dev, NULL, &ops); 82571e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy)) 82671e2f5c5SKishon Vijay Abraham I return PTR_ERR(phy); 82771e2f5c5SKishon Vijay Abraham I 82871e2f5c5SKishon Vijay Abraham I phy_set_drvdata(phy, am654_phy); 82971e2f5c5SKishon Vijay Abraham I phy_provider = devm_of_phy_provider_register(dev, serdes_am654_xlate); 83071e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy_provider)) { 83171e2f5c5SKishon Vijay Abraham I ret = PTR_ERR(phy_provider); 83271e2f5c5SKishon Vijay Abraham I goto clk_err; 83371e2f5c5SKishon Vijay Abraham I } 83471e2f5c5SKishon Vijay Abraham I 83571e2f5c5SKishon Vijay Abraham I return 0; 83671e2f5c5SKishon Vijay Abraham I 83771e2f5c5SKishon Vijay Abraham I clk_err: 83871e2f5c5SKishon Vijay Abraham I of_clk_del_provider(node); 83971e2f5c5SKishon Vijay Abraham I 84071e2f5c5SKishon Vijay Abraham I return ret; 84171e2f5c5SKishon Vijay Abraham I } 84271e2f5c5SKishon Vijay Abraham I 84371e2f5c5SKishon Vijay Abraham I static int serdes_am654_remove(struct platform_device *pdev) 84471e2f5c5SKishon Vijay Abraham I { 84571e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy = platform_get_drvdata(pdev); 84671e2f5c5SKishon Vijay Abraham I struct device_node *node = am654_phy->of_node; 84771e2f5c5SKishon Vijay Abraham I 84871e2f5c5SKishon Vijay Abraham I pm_runtime_disable(&pdev->dev); 84971e2f5c5SKishon Vijay Abraham I of_clk_del_provider(node); 85071e2f5c5SKishon Vijay Abraham I 85171e2f5c5SKishon Vijay Abraham I return 0; 85271e2f5c5SKishon Vijay Abraham I } 85371e2f5c5SKishon Vijay Abraham I 85471e2f5c5SKishon Vijay Abraham I static struct platform_driver serdes_am654_driver = { 85571e2f5c5SKishon Vijay Abraham I .probe = serdes_am654_probe, 85671e2f5c5SKishon Vijay Abraham I .remove = serdes_am654_remove, 85771e2f5c5SKishon Vijay Abraham I .driver = { 85871e2f5c5SKishon Vijay Abraham I .name = "phy-am654", 85971e2f5c5SKishon Vijay Abraham I .of_match_table = serdes_am654_id_table, 86071e2f5c5SKishon Vijay Abraham I }, 86171e2f5c5SKishon Vijay Abraham I }; 86271e2f5c5SKishon Vijay Abraham I module_platform_driver(serdes_am654_driver); 86371e2f5c5SKishon Vijay Abraham I 86471e2f5c5SKishon Vijay Abraham I MODULE_AUTHOR("Texas Instruments Inc."); 86571e2f5c5SKishon Vijay Abraham I MODULE_DESCRIPTION("TI AM654x SERDES driver"); 86671e2f5c5SKishon Vijay Abraham I MODULE_LICENSE("GPL v2"); 867