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 2271e2f5c5SKishon Vijay Abraham I #define CMU_R07C 0x7c 2371e2f5c5SKishon Vijay Abraham I 2471e2f5c5SKishon Vijay Abraham I #define COMLANE_R138 0xb38 2571e2f5c5SKishon Vijay Abraham I #define VERSION 0x70 2671e2f5c5SKishon Vijay Abraham I 2771e2f5c5SKishon Vijay Abraham I #define COMLANE_R190 0xb90 2871e2f5c5SKishon Vijay Abraham I 2971e2f5c5SKishon Vijay Abraham I #define COMLANE_R194 0xb94 3071e2f5c5SKishon Vijay Abraham I 3171e2f5c5SKishon Vijay Abraham I #define SERDES_CTRL 0x1fd0 3271e2f5c5SKishon Vijay Abraham I 3371e2f5c5SKishon Vijay Abraham I #define WIZ_LANEXCTL_STS 0x1fe0 3471e2f5c5SKishon Vijay Abraham I #define TX0_DISABLE_STATE 0x4 3571e2f5c5SKishon Vijay Abraham I #define TX0_SLEEP_STATE 0x5 3671e2f5c5SKishon Vijay Abraham I #define TX0_SNOOZE_STATE 0x6 3771e2f5c5SKishon Vijay Abraham I #define TX0_ENABLE_STATE 0x7 3871e2f5c5SKishon Vijay Abraham I 3971e2f5c5SKishon Vijay Abraham I #define RX0_DISABLE_STATE 0x4 4071e2f5c5SKishon Vijay Abraham I #define RX0_SLEEP_STATE 0x5 4171e2f5c5SKishon Vijay Abraham I #define RX0_SNOOZE_STATE 0x6 4271e2f5c5SKishon Vijay Abraham I #define RX0_ENABLE_STATE 0x7 4371e2f5c5SKishon Vijay Abraham I 4471e2f5c5SKishon Vijay Abraham I #define WIZ_PLL_CTRL 0x1ff4 4571e2f5c5SKishon Vijay Abraham I #define PLL_DISABLE_STATE 0x4 4671e2f5c5SKishon Vijay Abraham I #define PLL_SLEEP_STATE 0x5 4771e2f5c5SKishon Vijay Abraham I #define PLL_SNOOZE_STATE 0x6 4871e2f5c5SKishon Vijay Abraham I #define PLL_ENABLE_STATE 0x7 4971e2f5c5SKishon Vijay Abraham I 5071e2f5c5SKishon Vijay Abraham I #define PLL_LOCK_TIME 100000 /* in microseconds */ 5171e2f5c5SKishon Vijay Abraham I #define SLEEP_TIME 100 /* in microseconds */ 5271e2f5c5SKishon Vijay Abraham I 5371e2f5c5SKishon Vijay Abraham I #define LANE_USB3 0x0 5471e2f5c5SKishon Vijay Abraham I #define LANE_PCIE0_LANE0 0x1 5571e2f5c5SKishon Vijay Abraham I 5671e2f5c5SKishon Vijay Abraham I #define LANE_PCIE1_LANE0 0x0 5771e2f5c5SKishon Vijay Abraham I #define LANE_PCIE0_LANE1 0x1 5871e2f5c5SKishon Vijay Abraham I 5971e2f5c5SKishon Vijay Abraham I #define SERDES_NUM_CLOCKS 3 6071e2f5c5SKishon Vijay Abraham I 617e7b8ca6SRoger Quadros #define AM654_SERDES_CTRL_CLKSEL_MASK GENMASK(7, 4) 627e7b8ca6SRoger Quadros #define AM654_SERDES_CTRL_CLKSEL_SHIFT 4 637e7b8ca6SRoger Quadros 6471e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux { 6571e2f5c5SKishon Vijay Abraham I struct clk_hw hw; 6671e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 6771e2f5c5SKishon Vijay Abraham I unsigned int reg; 687e7b8ca6SRoger Quadros int clk_id; 6971e2f5c5SKishon Vijay Abraham I struct clk_init_data clk_data; 7071e2f5c5SKishon Vijay Abraham I }; 7171e2f5c5SKishon Vijay Abraham I 7271e2f5c5SKishon Vijay Abraham I #define to_serdes_am654_clk_mux(_hw) \ 7371e2f5c5SKishon Vijay Abraham I container_of(_hw, struct serdes_am654_clk_mux, hw) 7471e2f5c5SKishon Vijay Abraham I 7571e2f5c5SKishon Vijay Abraham I static struct regmap_config serdes_am654_regmap_config = { 7671e2f5c5SKishon Vijay Abraham I .reg_bits = 32, 7771e2f5c5SKishon Vijay Abraham I .val_bits = 32, 7871e2f5c5SKishon Vijay Abraham I .reg_stride = 4, 7971e2f5c5SKishon Vijay Abraham I .fast_io = true, 8071e2f5c5SKishon Vijay Abraham I }; 8171e2f5c5SKishon Vijay Abraham I 8271e2f5c5SKishon Vijay Abraham I static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24); 8371e2f5c5SKishon Vijay Abraham I static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23); 8471e2f5c5SKishon Vijay Abraham I static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9); 8571e2f5c5SKishon Vijay Abraham I static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19); 8671e2f5c5SKishon Vijay Abraham I static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29); 8771e2f5c5SKishon Vijay Abraham I static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31); 8871e2f5c5SKishon Vijay Abraham I static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15); 8971e2f5c5SKishon Vijay Abraham I static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31); 9071e2f5c5SKishon Vijay Abraham I static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28); 9171e2f5c5SKishon Vijay Abraham I 9271e2f5c5SKishon Vijay Abraham I struct serdes_am654 { 9371e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 9471e2f5c5SKishon Vijay Abraham I struct regmap_field *cmu_master_cdn_o; 9571e2f5c5SKishon Vijay Abraham I struct regmap_field *config_version; 9671e2f5c5SKishon Vijay Abraham I struct regmap_field *l1_master_cdn_o; 9771e2f5c5SKishon Vijay Abraham I struct regmap_field *cmu_ok_i_0; 9871e2f5c5SKishon Vijay Abraham I struct regmap_field *por_en; 9971e2f5c5SKishon Vijay Abraham I struct regmap_field *tx0_enable; 10071e2f5c5SKishon Vijay Abraham I struct regmap_field *rx0_enable; 10171e2f5c5SKishon Vijay Abraham I struct regmap_field *pll_enable; 10271e2f5c5SKishon Vijay Abraham I struct regmap_field *pll_ok; 10371e2f5c5SKishon Vijay Abraham I 10471e2f5c5SKishon Vijay Abraham I struct device *dev; 10571e2f5c5SKishon Vijay Abraham I struct mux_control *control; 10671e2f5c5SKishon Vijay Abraham I bool busy; 10771e2f5c5SKishon Vijay Abraham I u32 type; 10871e2f5c5SKishon Vijay Abraham I struct device_node *of_node; 10971e2f5c5SKishon Vijay Abraham I struct clk_onecell_data clk_data; 11071e2f5c5SKishon Vijay Abraham I struct clk *clks[SERDES_NUM_CLOCKS]; 11171e2f5c5SKishon Vijay Abraham I }; 11271e2f5c5SKishon Vijay Abraham I 11371e2f5c5SKishon Vijay Abraham I static int serdes_am654_enable_pll(struct serdes_am654 *phy) 11471e2f5c5SKishon Vijay Abraham I { 11571e2f5c5SKishon Vijay Abraham I int ret; 11671e2f5c5SKishon Vijay Abraham I u32 val; 11771e2f5c5SKishon Vijay Abraham I 11871e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE); 11971e2f5c5SKishon Vijay Abraham I if (ret) 12071e2f5c5SKishon Vijay Abraham I return ret; 12171e2f5c5SKishon Vijay Abraham I 12271e2f5c5SKishon Vijay Abraham I return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000, 12371e2f5c5SKishon Vijay Abraham I PLL_LOCK_TIME); 12471e2f5c5SKishon Vijay Abraham I } 12571e2f5c5SKishon Vijay Abraham I 12671e2f5c5SKishon Vijay Abraham I static void serdes_am654_disable_pll(struct serdes_am654 *phy) 12771e2f5c5SKishon Vijay Abraham I { 12871e2f5c5SKishon Vijay Abraham I struct device *dev = phy->dev; 12971e2f5c5SKishon Vijay Abraham I int ret; 13071e2f5c5SKishon Vijay Abraham I 13171e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE); 13271e2f5c5SKishon Vijay Abraham I if (ret) 13371e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to disable PLL\n"); 13471e2f5c5SKishon Vijay Abraham I } 13571e2f5c5SKishon Vijay Abraham I 13671e2f5c5SKishon Vijay Abraham I static int serdes_am654_enable_txrx(struct serdes_am654 *phy) 13771e2f5c5SKishon Vijay Abraham I { 13871e2f5c5SKishon Vijay Abraham I int ret; 13971e2f5c5SKishon Vijay Abraham I 14071e2f5c5SKishon Vijay Abraham I /* Enable TX */ 14171e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE); 14271e2f5c5SKishon Vijay Abraham I if (ret) 14371e2f5c5SKishon Vijay Abraham I return ret; 14471e2f5c5SKishon Vijay Abraham I 14571e2f5c5SKishon Vijay Abraham I /* Enable RX */ 14671e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE); 14771e2f5c5SKishon Vijay Abraham I if (ret) 14871e2f5c5SKishon Vijay Abraham I return ret; 14971e2f5c5SKishon Vijay Abraham I 15071e2f5c5SKishon Vijay Abraham I return 0; 15171e2f5c5SKishon Vijay Abraham I } 15271e2f5c5SKishon Vijay Abraham I 15371e2f5c5SKishon Vijay Abraham I static int serdes_am654_disable_txrx(struct serdes_am654 *phy) 15471e2f5c5SKishon Vijay Abraham I { 15571e2f5c5SKishon Vijay Abraham I int ret; 15671e2f5c5SKishon Vijay Abraham I 15771e2f5c5SKishon Vijay Abraham I /* Disable TX */ 15871e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE); 15971e2f5c5SKishon Vijay Abraham I if (ret) 16071e2f5c5SKishon Vijay Abraham I return ret; 16171e2f5c5SKishon Vijay Abraham I 16271e2f5c5SKishon Vijay Abraham I /* Disable RX */ 16371e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE); 16471e2f5c5SKishon Vijay Abraham I if (ret) 16571e2f5c5SKishon Vijay Abraham I return ret; 16671e2f5c5SKishon Vijay Abraham I 16771e2f5c5SKishon Vijay Abraham I return 0; 16871e2f5c5SKishon Vijay Abraham I } 16971e2f5c5SKishon Vijay Abraham I 17071e2f5c5SKishon Vijay Abraham I static int serdes_am654_power_on(struct phy *x) 17171e2f5c5SKishon Vijay Abraham I { 17271e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 17371e2f5c5SKishon Vijay Abraham I struct device *dev = phy->dev; 17471e2f5c5SKishon Vijay Abraham I int ret; 17571e2f5c5SKishon Vijay Abraham I u32 val; 17671e2f5c5SKishon Vijay Abraham I 17771e2f5c5SKishon Vijay Abraham I ret = serdes_am654_enable_pll(phy); 17871e2f5c5SKishon Vijay Abraham I if (ret) { 17971e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to enable PLL\n"); 18071e2f5c5SKishon Vijay Abraham I return ret; 18171e2f5c5SKishon Vijay Abraham I } 18271e2f5c5SKishon Vijay Abraham I 18371e2f5c5SKishon Vijay Abraham I ret = serdes_am654_enable_txrx(phy); 18471e2f5c5SKishon Vijay Abraham I if (ret) { 18571e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to enable TX RX\n"); 18671e2f5c5SKishon Vijay Abraham I return ret; 18771e2f5c5SKishon Vijay Abraham I } 18871e2f5c5SKishon Vijay Abraham I 18971e2f5c5SKishon Vijay Abraham I return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val, 19071e2f5c5SKishon Vijay Abraham I SLEEP_TIME, PLL_LOCK_TIME); 19171e2f5c5SKishon Vijay Abraham I } 19271e2f5c5SKishon Vijay Abraham I 19371e2f5c5SKishon Vijay Abraham I static int serdes_am654_power_off(struct phy *x) 19471e2f5c5SKishon Vijay Abraham I { 19571e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 19671e2f5c5SKishon Vijay Abraham I 19771e2f5c5SKishon Vijay Abraham I serdes_am654_disable_txrx(phy); 19871e2f5c5SKishon Vijay Abraham I serdes_am654_disable_pll(phy); 19971e2f5c5SKishon Vijay Abraham I 20071e2f5c5SKishon Vijay Abraham I return 0; 20171e2f5c5SKishon Vijay Abraham I } 20271e2f5c5SKishon Vijay Abraham I 20371e2f5c5SKishon Vijay Abraham I static int serdes_am654_init(struct phy *x) 20471e2f5c5SKishon Vijay Abraham I { 20571e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 20671e2f5c5SKishon Vijay Abraham I int ret; 20771e2f5c5SKishon Vijay Abraham I 20871e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->config_version, VERSION); 20971e2f5c5SKishon Vijay Abraham I if (ret) 21071e2f5c5SKishon Vijay Abraham I return ret; 21171e2f5c5SKishon Vijay Abraham I 21271e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1); 21371e2f5c5SKishon Vijay Abraham I if (ret) 21471e2f5c5SKishon Vijay Abraham I return ret; 21571e2f5c5SKishon Vijay Abraham I 21671e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->l1_master_cdn_o, 0x1); 21771e2f5c5SKishon Vijay Abraham I if (ret) 21871e2f5c5SKishon Vijay Abraham I return ret; 21971e2f5c5SKishon Vijay Abraham I 22071e2f5c5SKishon Vijay Abraham I return 0; 22171e2f5c5SKishon Vijay Abraham I } 22271e2f5c5SKishon Vijay Abraham I 22371e2f5c5SKishon Vijay Abraham I static int serdes_am654_reset(struct phy *x) 22471e2f5c5SKishon Vijay Abraham I { 22571e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 22671e2f5c5SKishon Vijay Abraham I int ret; 22771e2f5c5SKishon Vijay Abraham I 22871e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->por_en, 0x1); 22971e2f5c5SKishon Vijay Abraham I if (ret) 23071e2f5c5SKishon Vijay Abraham I return ret; 23171e2f5c5SKishon Vijay Abraham I 23271e2f5c5SKishon Vijay Abraham I mdelay(1); 23371e2f5c5SKishon Vijay Abraham I 23471e2f5c5SKishon Vijay Abraham I ret = regmap_field_write(phy->por_en, 0x0); 23571e2f5c5SKishon Vijay Abraham I if (ret) 23671e2f5c5SKishon Vijay Abraham I return ret; 23771e2f5c5SKishon Vijay Abraham I 23871e2f5c5SKishon Vijay Abraham I return 0; 23971e2f5c5SKishon Vijay Abraham I } 24071e2f5c5SKishon Vijay Abraham I 24171e2f5c5SKishon Vijay Abraham I static void serdes_am654_release(struct phy *x) 24271e2f5c5SKishon Vijay Abraham I { 24371e2f5c5SKishon Vijay Abraham I struct serdes_am654 *phy = phy_get_drvdata(x); 24471e2f5c5SKishon Vijay Abraham I 24571e2f5c5SKishon Vijay Abraham I phy->type = PHY_NONE; 24671e2f5c5SKishon Vijay Abraham I phy->busy = false; 24771e2f5c5SKishon Vijay Abraham I mux_control_deselect(phy->control); 24871e2f5c5SKishon Vijay Abraham I } 24971e2f5c5SKishon Vijay Abraham I 25071e2f5c5SKishon Vijay Abraham I struct phy *serdes_am654_xlate(struct device *dev, struct of_phandle_args 25171e2f5c5SKishon Vijay Abraham I *args) 25271e2f5c5SKishon Vijay Abraham I { 25371e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy; 25471e2f5c5SKishon Vijay Abraham I struct phy *phy; 25571e2f5c5SKishon Vijay Abraham I int ret; 25671e2f5c5SKishon Vijay Abraham I 25771e2f5c5SKishon Vijay Abraham I phy = of_phy_simple_xlate(dev, args); 25871e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy)) 25971e2f5c5SKishon Vijay Abraham I return phy; 26071e2f5c5SKishon Vijay Abraham I 26171e2f5c5SKishon Vijay Abraham I am654_phy = phy_get_drvdata(phy); 26271e2f5c5SKishon Vijay Abraham I if (am654_phy->busy) 26371e2f5c5SKishon Vijay Abraham I return ERR_PTR(-EBUSY); 26471e2f5c5SKishon Vijay Abraham I 26571e2f5c5SKishon Vijay Abraham I ret = mux_control_select(am654_phy->control, args->args[1]); 26671e2f5c5SKishon Vijay Abraham I if (ret) { 26771e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to select SERDES Lane Function\n"); 26871e2f5c5SKishon Vijay Abraham I return ERR_PTR(ret); 26971e2f5c5SKishon Vijay Abraham I } 27071e2f5c5SKishon Vijay Abraham I 27171e2f5c5SKishon Vijay Abraham I am654_phy->busy = true; 27271e2f5c5SKishon Vijay Abraham I am654_phy->type = args->args[0]; 27371e2f5c5SKishon Vijay Abraham I 27471e2f5c5SKishon Vijay Abraham I return phy; 27571e2f5c5SKishon Vijay Abraham I } 27671e2f5c5SKishon Vijay Abraham I 27771e2f5c5SKishon Vijay Abraham I static const struct phy_ops ops = { 27871e2f5c5SKishon Vijay Abraham I .reset = serdes_am654_reset, 27971e2f5c5SKishon Vijay Abraham I .init = serdes_am654_init, 28071e2f5c5SKishon Vijay Abraham I .power_on = serdes_am654_power_on, 28171e2f5c5SKishon Vijay Abraham I .power_off = serdes_am654_power_off, 28271e2f5c5SKishon Vijay Abraham I .release = serdes_am654_release, 28371e2f5c5SKishon Vijay Abraham I .owner = THIS_MODULE, 28471e2f5c5SKishon Vijay Abraham I }; 28571e2f5c5SKishon Vijay Abraham I 2867e7b8ca6SRoger Quadros #define SERDES_NUM_MUX_COMBINATIONS 16 2877e7b8ca6SRoger Quadros 2887e7b8ca6SRoger Quadros #define LICLK 0 2897e7b8ca6SRoger Quadros #define EXT_REFCLK 1 2907e7b8ca6SRoger Quadros #define RICLK 2 2917e7b8ca6SRoger Quadros 2927e7b8ca6SRoger Quadros static const int 2937e7b8ca6SRoger Quadros serdes_am654_mux_table[SERDES_NUM_MUX_COMBINATIONS][SERDES_NUM_CLOCKS] = { 2947e7b8ca6SRoger Quadros /* 2957e7b8ca6SRoger Quadros * Each combination maps to one of 2967e7b8ca6SRoger Quadros * "Figure 12-1986. SerDes Reference Clock Distribution" 2977e7b8ca6SRoger Quadros * in TRM. 2987e7b8ca6SRoger Quadros */ 2997e7b8ca6SRoger Quadros /* Parent of CMU refclk, Left output, Right output 3007e7b8ca6SRoger Quadros * either of EXT_REFCLK, LICLK, RICLK 3017e7b8ca6SRoger Quadros */ 3027e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0000 */ 3037e7b8ca6SRoger Quadros { RICLK, EXT_REFCLK, EXT_REFCLK }, /* 0001 */ 3047e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, LICLK }, /* 0010 */ 3057e7b8ca6SRoger Quadros { RICLK, RICLK, EXT_REFCLK }, /* 0011 */ 3067e7b8ca6SRoger Quadros { LICLK, EXT_REFCLK, EXT_REFCLK }, /* 0100 */ 3077e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0101 */ 3087e7b8ca6SRoger Quadros { LICLK, RICLK, LICLK }, /* 0110 */ 3097e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, LICLK }, /* 0111 */ 3107e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1000 */ 3117e7b8ca6SRoger Quadros { RICLK, EXT_REFCLK, LICLK }, /* 1001 */ 3127e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1010 */ 3137e7b8ca6SRoger Quadros { RICLK, RICLK, EXT_REFCLK }, /* 1011 */ 3147e7b8ca6SRoger Quadros { LICLK, EXT_REFCLK, LICLK }, /* 1100 */ 3157e7b8ca6SRoger Quadros { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1101 */ 3167e7b8ca6SRoger Quadros { LICLK, RICLK, EXT_REFCLK }, /* 1110 */ 3177e7b8ca6SRoger Quadros { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1111 */ 3187e7b8ca6SRoger Quadros }; 3197e7b8ca6SRoger Quadros 32071e2f5c5SKishon Vijay Abraham I static u8 serdes_am654_clk_mux_get_parent(struct clk_hw *hw) 32171e2f5c5SKishon Vijay Abraham I { 32271e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); 32371e2f5c5SKishon Vijay Abraham I struct regmap *regmap = mux->regmap; 32471e2f5c5SKishon Vijay Abraham I unsigned int reg = mux->reg; 32571e2f5c5SKishon Vijay Abraham I unsigned int val; 32671e2f5c5SKishon Vijay Abraham I 32771e2f5c5SKishon Vijay Abraham I regmap_read(regmap, reg, &val); 3287e7b8ca6SRoger Quadros val &= AM654_SERDES_CTRL_CLKSEL_MASK; 3297e7b8ca6SRoger Quadros val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; 33071e2f5c5SKishon Vijay Abraham I 3317e7b8ca6SRoger Quadros return serdes_am654_mux_table[val][mux->clk_id]; 33271e2f5c5SKishon Vijay Abraham I } 33371e2f5c5SKishon Vijay Abraham I 33471e2f5c5SKishon Vijay Abraham I static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index) 33571e2f5c5SKishon Vijay Abraham I { 33671e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); 33771e2f5c5SKishon Vijay Abraham I struct regmap *regmap = mux->regmap; 33871e2f5c5SKishon Vijay Abraham I unsigned int reg = mux->reg; 3397e7b8ca6SRoger Quadros int clk_id = mux->clk_id; 3407e7b8ca6SRoger Quadros int parents[SERDES_NUM_CLOCKS]; 3417e7b8ca6SRoger Quadros const int *p; 3427e7b8ca6SRoger Quadros u32 val; 3437e7b8ca6SRoger Quadros int found, i; 34471e2f5c5SKishon Vijay Abraham I int ret; 34571e2f5c5SKishon Vijay Abraham I 3467e7b8ca6SRoger Quadros /* get existing setting */ 3477e7b8ca6SRoger Quadros regmap_read(regmap, reg, &val); 3487e7b8ca6SRoger Quadros val &= AM654_SERDES_CTRL_CLKSEL_MASK; 3497e7b8ca6SRoger Quadros val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; 35071e2f5c5SKishon Vijay Abraham I 3517e7b8ca6SRoger Quadros for (i = 0; i < SERDES_NUM_CLOCKS; i++) 3527e7b8ca6SRoger Quadros parents[i] = serdes_am654_mux_table[val][i]; 3537e7b8ca6SRoger Quadros 3547e7b8ca6SRoger Quadros /* change parent of this clock. others left intact */ 3557e7b8ca6SRoger Quadros parents[clk_id] = index; 3567e7b8ca6SRoger Quadros 3577e7b8ca6SRoger Quadros /* Find the match */ 3587e7b8ca6SRoger Quadros for (val = 0; val < SERDES_NUM_MUX_COMBINATIONS; val++) { 3597e7b8ca6SRoger Quadros p = serdes_am654_mux_table[val]; 3607e7b8ca6SRoger Quadros found = 1; 3617e7b8ca6SRoger Quadros for (i = 0; i < SERDES_NUM_CLOCKS; i++) { 3627e7b8ca6SRoger Quadros if (parents[i] != p[i]) { 3637e7b8ca6SRoger Quadros found = 0; 3647e7b8ca6SRoger Quadros break; 3657e7b8ca6SRoger Quadros } 3667e7b8ca6SRoger Quadros } 3677e7b8ca6SRoger Quadros 3687e7b8ca6SRoger Quadros if (found) 3697e7b8ca6SRoger Quadros break; 3707e7b8ca6SRoger Quadros } 3717e7b8ca6SRoger Quadros 3727e7b8ca6SRoger Quadros if (!found) { 3737e7b8ca6SRoger Quadros /* 3747e7b8ca6SRoger Quadros * This can never happen, unless we missed 3757e7b8ca6SRoger Quadros * a valid combination in serdes_am654_mux_table. 3767e7b8ca6SRoger Quadros */ 3777e7b8ca6SRoger Quadros WARN(1, "Failed to find the parent of %s clock\n", 3787e7b8ca6SRoger Quadros hw->init->name); 37971e2f5c5SKishon Vijay Abraham I return -EINVAL; 3807e7b8ca6SRoger Quadros } 38171e2f5c5SKishon Vijay Abraham I 3827e7b8ca6SRoger Quadros val <<= AM654_SERDES_CTRL_CLKSEL_SHIFT; 3837e7b8ca6SRoger Quadros ret = regmap_update_bits(regmap, reg, AM654_SERDES_CTRL_CLKSEL_MASK, 3847e7b8ca6SRoger Quadros val); 38571e2f5c5SKishon Vijay Abraham I 38671e2f5c5SKishon Vijay Abraham I return ret; 38771e2f5c5SKishon Vijay Abraham I } 38871e2f5c5SKishon Vijay Abraham I 38971e2f5c5SKishon Vijay Abraham I static const struct clk_ops serdes_am654_clk_mux_ops = { 39071e2f5c5SKishon Vijay Abraham I .set_parent = serdes_am654_clk_mux_set_parent, 39171e2f5c5SKishon Vijay Abraham I .get_parent = serdes_am654_clk_mux_get_parent, 39271e2f5c5SKishon Vijay Abraham I }; 39371e2f5c5SKishon Vijay Abraham I 39471e2f5c5SKishon Vijay Abraham I static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, 39571e2f5c5SKishon Vijay Abraham I const char *clock_name, int clock_num) 39671e2f5c5SKishon Vijay Abraham I { 39771e2f5c5SKishon Vijay Abraham I struct device_node *node = am654_phy->of_node; 39871e2f5c5SKishon Vijay Abraham I struct device *dev = am654_phy->dev; 39971e2f5c5SKishon Vijay Abraham I struct serdes_am654_clk_mux *mux; 40071e2f5c5SKishon Vijay Abraham I struct device_node *regmap_node; 40171e2f5c5SKishon Vijay Abraham I const char **parent_names; 40271e2f5c5SKishon Vijay Abraham I struct clk_init_data *init; 40371e2f5c5SKishon Vijay Abraham I unsigned int num_parents; 40471e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 40571e2f5c5SKishon Vijay Abraham I const __be32 *addr; 40671e2f5c5SKishon Vijay Abraham I unsigned int reg; 40771e2f5c5SKishon Vijay Abraham I struct clk *clk; 40871e2f5c5SKishon Vijay Abraham I 40971e2f5c5SKishon Vijay Abraham I mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 41071e2f5c5SKishon Vijay Abraham I if (!mux) 41171e2f5c5SKishon Vijay Abraham I return -ENOMEM; 41271e2f5c5SKishon Vijay Abraham I 41371e2f5c5SKishon Vijay Abraham I init = &mux->clk_data; 41471e2f5c5SKishon Vijay Abraham I 41571e2f5c5SKishon Vijay Abraham I regmap_node = of_parse_phandle(node, "ti,serdes-clk", 0); 41671e2f5c5SKishon Vijay Abraham I of_node_put(regmap_node); 41771e2f5c5SKishon Vijay Abraham I if (!regmap_node) { 41871e2f5c5SKishon Vijay Abraham I dev_err(dev, "Fail to get serdes-clk node\n"); 41971e2f5c5SKishon Vijay Abraham I return -ENODEV; 42071e2f5c5SKishon Vijay Abraham I } 42171e2f5c5SKishon Vijay Abraham I 42271e2f5c5SKishon Vijay Abraham I regmap = syscon_node_to_regmap(regmap_node->parent); 42371e2f5c5SKishon Vijay Abraham I if (IS_ERR(regmap)) { 42471e2f5c5SKishon Vijay Abraham I dev_err(dev, "Fail to get Syscon regmap\n"); 42571e2f5c5SKishon Vijay Abraham I return PTR_ERR(regmap); 42671e2f5c5SKishon Vijay Abraham I } 42771e2f5c5SKishon Vijay Abraham I 42871e2f5c5SKishon Vijay Abraham I num_parents = of_clk_get_parent_count(node); 42971e2f5c5SKishon Vijay Abraham I if (num_parents < 2) { 43071e2f5c5SKishon Vijay Abraham I dev_err(dev, "SERDES clock must have parents\n"); 43171e2f5c5SKishon Vijay Abraham I return -EINVAL; 43271e2f5c5SKishon Vijay Abraham I } 43371e2f5c5SKishon Vijay Abraham I 43471e2f5c5SKishon Vijay Abraham I parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), 43571e2f5c5SKishon Vijay Abraham I GFP_KERNEL); 43671e2f5c5SKishon Vijay Abraham I if (!parent_names) 43771e2f5c5SKishon Vijay Abraham I return -ENOMEM; 43871e2f5c5SKishon Vijay Abraham I 43971e2f5c5SKishon Vijay Abraham I of_clk_parent_fill(node, parent_names, num_parents); 44071e2f5c5SKishon Vijay Abraham I 44171e2f5c5SKishon Vijay Abraham I addr = of_get_address(regmap_node, 0, NULL, NULL); 44271e2f5c5SKishon Vijay Abraham I if (!addr) 44371e2f5c5SKishon Vijay Abraham I return -EINVAL; 44471e2f5c5SKishon Vijay Abraham I 44571e2f5c5SKishon Vijay Abraham I reg = be32_to_cpu(*addr); 44671e2f5c5SKishon Vijay Abraham I 44771e2f5c5SKishon Vijay Abraham I init->ops = &serdes_am654_clk_mux_ops; 44871e2f5c5SKishon Vijay Abraham I init->flags = CLK_SET_RATE_NO_REPARENT; 44971e2f5c5SKishon Vijay Abraham I init->parent_names = parent_names; 45071e2f5c5SKishon Vijay Abraham I init->num_parents = num_parents; 45171e2f5c5SKishon Vijay Abraham I init->name = clock_name; 45271e2f5c5SKishon Vijay Abraham I 45371e2f5c5SKishon Vijay Abraham I mux->regmap = regmap; 45471e2f5c5SKishon Vijay Abraham I mux->reg = reg; 4557e7b8ca6SRoger Quadros mux->clk_id = clock_num; 45671e2f5c5SKishon Vijay Abraham I mux->hw.init = init; 45771e2f5c5SKishon Vijay Abraham I 45871e2f5c5SKishon Vijay Abraham I clk = devm_clk_register(dev, &mux->hw); 45971e2f5c5SKishon Vijay Abraham I if (IS_ERR(clk)) 46071e2f5c5SKishon Vijay Abraham I return PTR_ERR(clk); 46171e2f5c5SKishon Vijay Abraham I 46271e2f5c5SKishon Vijay Abraham I am654_phy->clks[clock_num] = clk; 46371e2f5c5SKishon Vijay Abraham I 46471e2f5c5SKishon Vijay Abraham I return 0; 46571e2f5c5SKishon Vijay Abraham I } 46671e2f5c5SKishon Vijay Abraham I 46771e2f5c5SKishon Vijay Abraham I static const struct of_device_id serdes_am654_id_table[] = { 46871e2f5c5SKishon Vijay Abraham I { 46971e2f5c5SKishon Vijay Abraham I .compatible = "ti,phy-am654-serdes", 47071e2f5c5SKishon Vijay Abraham I }, 47171e2f5c5SKishon Vijay Abraham I {} 47271e2f5c5SKishon Vijay Abraham I }; 47371e2f5c5SKishon Vijay Abraham I MODULE_DEVICE_TABLE(of, serdes_am654_id_table); 47471e2f5c5SKishon Vijay Abraham I 47571e2f5c5SKishon Vijay Abraham I static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy) 47671e2f5c5SKishon Vijay Abraham I { 47771e2f5c5SKishon Vijay Abraham I struct regmap *regmap = am654_phy->regmap; 47871e2f5c5SKishon Vijay Abraham I struct device *dev = am654_phy->dev; 47971e2f5c5SKishon Vijay Abraham I 48071e2f5c5SKishon Vijay Abraham I am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap, 48171e2f5c5SKishon Vijay Abraham I cmu_master_cdn_o); 48271e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->cmu_master_cdn_o)) { 48371e2f5c5SKishon Vijay Abraham I dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n"); 48471e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->cmu_master_cdn_o); 48571e2f5c5SKishon Vijay Abraham I } 48671e2f5c5SKishon Vijay Abraham I 48771e2f5c5SKishon Vijay Abraham I am654_phy->config_version = devm_regmap_field_alloc(dev, regmap, 48871e2f5c5SKishon Vijay Abraham I config_version); 48971e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->config_version)) { 49071e2f5c5SKishon Vijay Abraham I dev_err(dev, "CONFIG_VERSION reg field init failed\n"); 49171e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->config_version); 49271e2f5c5SKishon Vijay Abraham I } 49371e2f5c5SKishon Vijay Abraham I 49471e2f5c5SKishon Vijay Abraham I am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap, 49571e2f5c5SKishon Vijay Abraham I l1_master_cdn_o); 49671e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->l1_master_cdn_o)) { 49771e2f5c5SKishon Vijay Abraham I dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n"); 49871e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->l1_master_cdn_o); 49971e2f5c5SKishon Vijay Abraham I } 50071e2f5c5SKishon Vijay Abraham I 50171e2f5c5SKishon Vijay Abraham I am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap, 50271e2f5c5SKishon Vijay Abraham I cmu_ok_i_0); 50371e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->cmu_ok_i_0)) { 50471e2f5c5SKishon Vijay Abraham I dev_err(dev, "CMU_OK_I_0 reg field init failed\n"); 50571e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->cmu_ok_i_0); 50671e2f5c5SKishon Vijay Abraham I } 50771e2f5c5SKishon Vijay Abraham I 50871e2f5c5SKishon Vijay Abraham I am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en); 50971e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->por_en)) { 51071e2f5c5SKishon Vijay Abraham I dev_err(dev, "POR_EN reg field init failed\n"); 51171e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->por_en); 51271e2f5c5SKishon Vijay Abraham I } 51371e2f5c5SKishon Vijay Abraham I 51471e2f5c5SKishon Vijay Abraham I am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap, 51571e2f5c5SKishon Vijay Abraham I tx0_enable); 51671e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->tx0_enable)) { 51771e2f5c5SKishon Vijay Abraham I dev_err(dev, "TX0_ENABLE reg field init failed\n"); 51871e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->tx0_enable); 51971e2f5c5SKishon Vijay Abraham I } 52071e2f5c5SKishon Vijay Abraham I 52171e2f5c5SKishon Vijay Abraham I am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap, 52271e2f5c5SKishon Vijay Abraham I rx0_enable); 52371e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->rx0_enable)) { 52471e2f5c5SKishon Vijay Abraham I dev_err(dev, "RX0_ENABLE reg field init failed\n"); 52571e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->rx0_enable); 52671e2f5c5SKishon Vijay Abraham I } 52771e2f5c5SKishon Vijay Abraham I 52871e2f5c5SKishon Vijay Abraham I am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap, 52971e2f5c5SKishon Vijay Abraham I pll_enable); 53071e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->pll_enable)) { 53171e2f5c5SKishon Vijay Abraham I dev_err(dev, "PLL_ENABLE reg field init failed\n"); 53271e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->pll_enable); 53371e2f5c5SKishon Vijay Abraham I } 53471e2f5c5SKishon Vijay Abraham I 53571e2f5c5SKishon Vijay Abraham I am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok); 53671e2f5c5SKishon Vijay Abraham I if (IS_ERR(am654_phy->pll_ok)) { 53771e2f5c5SKishon Vijay Abraham I dev_err(dev, "PLL_OK reg field init failed\n"); 53871e2f5c5SKishon Vijay Abraham I return PTR_ERR(am654_phy->pll_ok); 53971e2f5c5SKishon Vijay Abraham I } 54071e2f5c5SKishon Vijay Abraham I 54171e2f5c5SKishon Vijay Abraham I return 0; 54271e2f5c5SKishon Vijay Abraham I } 54371e2f5c5SKishon Vijay Abraham I 54471e2f5c5SKishon Vijay Abraham I static int serdes_am654_probe(struct platform_device *pdev) 54571e2f5c5SKishon Vijay Abraham I { 54671e2f5c5SKishon Vijay Abraham I struct phy_provider *phy_provider; 54771e2f5c5SKishon Vijay Abraham I struct device *dev = &pdev->dev; 54871e2f5c5SKishon Vijay Abraham I struct device_node *node = dev->of_node; 54971e2f5c5SKishon Vijay Abraham I struct clk_onecell_data *clk_data; 55071e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy; 55171e2f5c5SKishon Vijay Abraham I struct mux_control *control; 55271e2f5c5SKishon Vijay Abraham I const char *clock_name; 55371e2f5c5SKishon Vijay Abraham I struct regmap *regmap; 55471e2f5c5SKishon Vijay Abraham I void __iomem *base; 55571e2f5c5SKishon Vijay Abraham I struct phy *phy; 55671e2f5c5SKishon Vijay Abraham I int ret; 55771e2f5c5SKishon Vijay Abraham I int i; 55871e2f5c5SKishon Vijay Abraham I 55971e2f5c5SKishon Vijay Abraham I am654_phy = devm_kzalloc(dev, sizeof(*am654_phy), GFP_KERNEL); 56071e2f5c5SKishon Vijay Abraham I if (!am654_phy) 56171e2f5c5SKishon Vijay Abraham I return -ENOMEM; 56271e2f5c5SKishon Vijay Abraham I 56371e2f5c5SKishon Vijay Abraham I base = devm_platform_ioremap_resource(pdev, 0); 56471e2f5c5SKishon Vijay Abraham I if (IS_ERR(base)) 56571e2f5c5SKishon Vijay Abraham I return PTR_ERR(base); 56671e2f5c5SKishon Vijay Abraham I 56771e2f5c5SKishon Vijay Abraham I regmap = devm_regmap_init_mmio(dev, base, &serdes_am654_regmap_config); 56871e2f5c5SKishon Vijay Abraham I if (IS_ERR(regmap)) { 56971e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize regmap\n"); 57071e2f5c5SKishon Vijay Abraham I return PTR_ERR(regmap); 57171e2f5c5SKishon Vijay Abraham I } 57271e2f5c5SKishon Vijay Abraham I 57371e2f5c5SKishon Vijay Abraham I control = devm_mux_control_get(dev, NULL); 57471e2f5c5SKishon Vijay Abraham I if (IS_ERR(control)) 57571e2f5c5SKishon Vijay Abraham I return PTR_ERR(control); 57671e2f5c5SKishon Vijay Abraham I 57771e2f5c5SKishon Vijay Abraham I am654_phy->dev = dev; 57871e2f5c5SKishon Vijay Abraham I am654_phy->of_node = node; 57971e2f5c5SKishon Vijay Abraham I am654_phy->regmap = regmap; 58071e2f5c5SKishon Vijay Abraham I am654_phy->control = control; 58171e2f5c5SKishon Vijay Abraham I am654_phy->type = PHY_NONE; 58271e2f5c5SKishon Vijay Abraham I 58371e2f5c5SKishon Vijay Abraham I ret = serdes_am654_regfield_init(am654_phy); 58471e2f5c5SKishon Vijay Abraham I if (ret) { 58571e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize regfields\n"); 58671e2f5c5SKishon Vijay Abraham I return ret; 58771e2f5c5SKishon Vijay Abraham I } 58871e2f5c5SKishon Vijay Abraham I 58971e2f5c5SKishon Vijay Abraham I platform_set_drvdata(pdev, am654_phy); 59071e2f5c5SKishon Vijay Abraham I 59171e2f5c5SKishon Vijay Abraham I for (i = 0; i < SERDES_NUM_CLOCKS; i++) { 59271e2f5c5SKishon Vijay Abraham I ret = of_property_read_string_index(node, "clock-output-names", 59371e2f5c5SKishon Vijay Abraham I i, &clock_name); 59471e2f5c5SKishon Vijay Abraham I if (ret) { 59571e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to get clock name\n"); 59671e2f5c5SKishon Vijay Abraham I return ret; 59771e2f5c5SKishon Vijay Abraham I } 59871e2f5c5SKishon Vijay Abraham I 59971e2f5c5SKishon Vijay Abraham I ret = serdes_am654_clk_register(am654_phy, clock_name, i); 60071e2f5c5SKishon Vijay Abraham I if (ret) { 60171e2f5c5SKishon Vijay Abraham I dev_err(dev, "Failed to initialize clock %s\n", 60271e2f5c5SKishon Vijay Abraham I clock_name); 60371e2f5c5SKishon Vijay Abraham I return ret; 60471e2f5c5SKishon Vijay Abraham I } 60571e2f5c5SKishon Vijay Abraham I } 60671e2f5c5SKishon Vijay Abraham I 60771e2f5c5SKishon Vijay Abraham I clk_data = &am654_phy->clk_data; 60871e2f5c5SKishon Vijay Abraham I clk_data->clks = am654_phy->clks; 60971e2f5c5SKishon Vijay Abraham I clk_data->clk_num = SERDES_NUM_CLOCKS; 61071e2f5c5SKishon Vijay Abraham I ret = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); 61171e2f5c5SKishon Vijay Abraham I if (ret) 61271e2f5c5SKishon Vijay Abraham I return ret; 61371e2f5c5SKishon Vijay Abraham I 61471e2f5c5SKishon Vijay Abraham I pm_runtime_enable(dev); 61571e2f5c5SKishon Vijay Abraham I 61671e2f5c5SKishon Vijay Abraham I phy = devm_phy_create(dev, NULL, &ops); 61771e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy)) 61871e2f5c5SKishon Vijay Abraham I return PTR_ERR(phy); 61971e2f5c5SKishon Vijay Abraham I 62071e2f5c5SKishon Vijay Abraham I phy_set_drvdata(phy, am654_phy); 62171e2f5c5SKishon Vijay Abraham I phy_provider = devm_of_phy_provider_register(dev, serdes_am654_xlate); 62271e2f5c5SKishon Vijay Abraham I if (IS_ERR(phy_provider)) { 62371e2f5c5SKishon Vijay Abraham I ret = PTR_ERR(phy_provider); 62471e2f5c5SKishon Vijay Abraham I goto clk_err; 62571e2f5c5SKishon Vijay Abraham I } 62671e2f5c5SKishon Vijay Abraham I 62771e2f5c5SKishon Vijay Abraham I return 0; 62871e2f5c5SKishon Vijay Abraham I 62971e2f5c5SKishon Vijay Abraham I clk_err: 63071e2f5c5SKishon Vijay Abraham I of_clk_del_provider(node); 63171e2f5c5SKishon Vijay Abraham I 63271e2f5c5SKishon Vijay Abraham I return ret; 63371e2f5c5SKishon Vijay Abraham I } 63471e2f5c5SKishon Vijay Abraham I 63571e2f5c5SKishon Vijay Abraham I static int serdes_am654_remove(struct platform_device *pdev) 63671e2f5c5SKishon Vijay Abraham I { 63771e2f5c5SKishon Vijay Abraham I struct serdes_am654 *am654_phy = platform_get_drvdata(pdev); 63871e2f5c5SKishon Vijay Abraham I struct device_node *node = am654_phy->of_node; 63971e2f5c5SKishon Vijay Abraham I 64071e2f5c5SKishon Vijay Abraham I pm_runtime_disable(&pdev->dev); 64171e2f5c5SKishon Vijay Abraham I of_clk_del_provider(node); 64271e2f5c5SKishon Vijay Abraham I 64371e2f5c5SKishon Vijay Abraham I return 0; 64471e2f5c5SKishon Vijay Abraham I } 64571e2f5c5SKishon Vijay Abraham I 64671e2f5c5SKishon Vijay Abraham I static struct platform_driver serdes_am654_driver = { 64771e2f5c5SKishon Vijay Abraham I .probe = serdes_am654_probe, 64871e2f5c5SKishon Vijay Abraham I .remove = serdes_am654_remove, 64971e2f5c5SKishon Vijay Abraham I .driver = { 65071e2f5c5SKishon Vijay Abraham I .name = "phy-am654", 65171e2f5c5SKishon Vijay Abraham I .of_match_table = serdes_am654_id_table, 65271e2f5c5SKishon Vijay Abraham I }, 65371e2f5c5SKishon Vijay Abraham I }; 65471e2f5c5SKishon Vijay Abraham I module_platform_driver(serdes_am654_driver); 65571e2f5c5SKishon Vijay Abraham I 65671e2f5c5SKishon Vijay Abraham I MODULE_AUTHOR("Texas Instruments Inc."); 65771e2f5c5SKishon Vijay Abraham I MODULE_DESCRIPTION("TI AM654x SERDES driver"); 65871e2f5c5SKishon Vijay Abraham I MODULE_LICENSE("GPL v2"); 659