From patchwork Wed Jun 21 03:16:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jit Loon Lim X-Patchwork-Id: 1797641 X-Patchwork-Delegate: marek.vasut@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=cGIiY4e0; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Qm7yz3Qbvz20XS for ; Wed, 21 Jun 2023 13:19:39 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id D08318619A; Wed, 21 Jun 2023 05:17:56 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="cGIiY4e0"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 6033086308; Wed, 21 Jun 2023 05:17:17 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE, SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 835B586383 for ; Wed, 21 Jun 2023 05:16:28 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: phobos.denx.de; spf=none smtp.mailfrom=jitloonl@ecsmtp.png.intel.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1687317388; x=1718853388; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=hs7rF8hN7/IRV6rNz6k1d2dRJV0BE8oOxxdJymFnizQ=; b=cGIiY4e0RcaLdKcXo5R+D2euqGR3y9EXkjyrRsniJC2X7wNHUN3EFCse PklM8hfSbvCBoLXy+mCGMCi/ZvSSJXstCvZg8hxOcgoJ93q1utoGoQLuI LibJ7vz4EmOtmex7aU/ppnaki+lapFgEqrMePjul/3yARJ5gAJ9s5PNjg eI/gxKNeqFf1FtWAwu3wQJF4YoajGxgSaZksHWNTt+oxLGRmCs6RJveqE jXqj8aFgXRHeH6xpwFSF3Y4C36sUjBM26eFJ4EWMKEO+gLd57+HewdYTp EYgL+RpSswkgIAvK5QgQLrThBhEehKiMz38Jy9mt338vU166gj9puRWOc w==; X-IronPort-AV: E=McAfee;i="6600,9927,10747"; a="360059787" X-IronPort-AV: E=Sophos;i="6.00,259,1681196400"; d="scan'208";a="360059787" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Jun 2023 20:16:26 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10747"; a="664479901" X-IronPort-AV: E=Sophos;i="6.00,259,1681196400"; d="scan'208";a="664479901" Received: from pglmail07.png.intel.com ([10.221.193.207]) by orsmga003.jf.intel.com with ESMTP; 20 Jun 2023 20:16:22 -0700 Received: from localhost (pgli0121.png.intel.com [10.221.240.84]) by pglmail07.png.intel.com (Postfix) with ESMTP id 2F49F2B70; Wed, 21 Jun 2023 11:16:22 +0800 (+08) Received: by localhost (Postfix, from userid 12048045) id 2D8302950; Wed, 21 Jun 2023 11:16:22 +0800 (+08) From: Jit Loon Lim To: u-boot@lists.denx.de Cc: Jagan Teki , Vignesh R , Marek , Simon , Tien Fong , Kok Kiang , Raaj , Dinesh , Boon Khai , Alif , Teik Heng , Hazim , Jit Loon Lim , Sieu Mun Tang Subject: [PATCH v1 12/17] drivers: phy: add combo phy driver for agilex5 Date: Wed, 21 Jun 2023 11:16:05 +0800 Message-Id: <20230621031610.28401-13-jit.loon.lim@intel.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20230621031610.28401-1-jit.loon.lim@intel.com> References: <20230621031610.28401-1-jit.loon.lim@intel.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean This is for new platform enablement for agilex5. Add combo phy driver for new platform. Signed-off-by: Jit Loon Lim --- drivers/phy/cadence/Kconfig | 9 + drivers/phy/cadence/Makefile | 1 + drivers/phy/cadence/phy-cadence-combophy.c | 855 +++++++++++++++++++++ 3 files changed, 865 insertions(+) create mode 100644 drivers/phy/cadence/phy-cadence-combophy.c diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index 549ddbf504..61d1b36be2 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -1,3 +1,12 @@ +config PHY_CADENCE_COMBOPHY + tristate "Cadence ComboPhy PHY Driver" + depends on MMC_SDHCI_CADENCE + help + Enable this to support the Cadence ComboPhy PHY driver + A single module that encapsulates all functionality required to interface + to external NAND Flash, eMMC devices, and SD cards for command and data + transfer. + config PHY_CADENCE_SIERRA tristate "Cadence Sierra PHY Driver" depends on DM_RESET diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index af63b32d9f..3092b9f3b8 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_$(SPL_)PHY_CADENCE_SIERRA) += phy-cadence-sierra.o obj-$(CONFIG_$(SPL_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o +obj-$(CONFIG_PHY_CADENCE_COMBOPHY) += phy-cadence-combophy.o diff --git a/drivers/phy/cadence/phy-cadence-combophy.c b/drivers/phy/cadence/phy-cadence-combophy.c new file mode 100644 index 0000000000..c49c45e5a0 --- /dev/null +++ b/drivers/phy/cadence/phy-cadence-combophy.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence COMBOPHY PHY Driver + * + * Based on the linux driver provided by Cadence Sierra + * + * Copyright (c) 2022 - 2023 Intel Corporation + * Author: Lee, Kah Jing + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PHY register offsets */ +/* General define */ +#define BIT_ZERO 0 +#define BIT_ONE 1 +#define SD_HOST_CLK 200000000 + +/* SDIO Card UHS-I modes */ +#define SDIO_MODE_SDR12 0 +#define SDIO_MODE_SDR25 1 +#define SDIO_MODE_SDR50 2 +#define SDIO_MODE_SDR104 3 +#define SDIO_MODE_DDR50 4 +#define SD_MODE_HS 5 + +/* eMMC modes */ +#define eMMC_MODE_SDR 0 +#define eMMC_MODE_HS 1 +#define eMMC_MODE_HS200 2 +#define eMMC_MODE_HS400 3 +/* SW_RESET_REG */ +#define SDHCI_CDNS_HRS00 0x00 +#define SDHCI_CDNS_HRS00_SWR BIT(0) +/* PHY access port */ +#define SDHCI_CDNS_HRS04 0x10 +#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0) +/* PHY data access port */ +#define SDHCI_CDNS_HRS05 0x14 +/* eMMC control registers */ +#define SDHCI_CDNS_HRS06 0x18 + +#define READ_OP 0 +#define WRITE_OP 1 +#define READ_WRITE_OP 2 +#define POLLING_OP 3 +#define READ_PHY_OP 4 +#define READ_WRITE_PHY_OP 5 + +#define PHY_TYPE_NAND 0 +#define PHY_TYPE_SDMMC 1 +#define PHY_REG_LEN 15 +/* IO_DELAY_INFO_REG */ +#define SDHCI_CDNS_HRS07 0x1c +#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16) +#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0) +/* TODO: check DV dfi_init val=9 */ +#define SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA 0x9 +/* TODO: check DV dfi_init val=8; DDR Mode */ +#define SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA_DDR 0x8 +#define SDHCI_CDNS_HRS07_IDELAY_VAL_DATA 0x0 +/* PHY reset port */ +#define SDHCI_CDNS_HRS09 0x24 +#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0) +#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1) +#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2) +#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3) +#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15) +#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16) + +/* PHY reset port */ +#define SDHCI_CDNS_HRS10 0x28 +#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16) +#define SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA 0x0 +/* HCSDCLKADJ DATA; DDR Mode */ +#define SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA_DDR 0x2 + +/* CMD_DATA_OUTPUT */ +#define SDHCI_CDNS_HRS16 0x40 +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY GENMASK(31, 28) +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY GENMASK(27, 24) +#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY GENMASK(23, 20) +#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY GENMASK(19, 16) +#define SDHCI_CDNS_HRS16_WRDATA1_DLY GENMASK(15, 12) +#define SDHCI_CDNS_HRS16_WRDATA0_DLY GENMASK(11, 8) +#define SDHCI_CDNS_HRS16_WRCMD1_DLY GENMASK(7, 4) +#define SDHCI_CDNS_HRS16_WRCMD0_DLY GENMASK(3, 0) +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA 0x0 +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA_DDR 0x1 +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA 0x0 +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA_DDR 0x1 +#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY_DATA 0x0 +#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY_DATA 0x0 +#define SDHCI_CDNS_HRS16_WRDATA1_DLY_DATA 0x0 +/* TODO: check DV dfi_init val=1 */ +#define SDHCI_CDNS_HRS16_WRDATA0_DLY_DATA 0x1 +#define SDHCI_CDNS_HRS16_WRCMD1_DLY_DATA 0x0 +/* TODO: check DV dfi_init val=1 */ +#define SDHCI_CDNS_HRS16_WRCMD0_DLY_DATA 0x1 + +/* SRS - Slot Register Set (SDHCI-compatible) */ +#define SDHCI_CDNS_SRS_BASE 0x200 +#define SDHCI_CDNS_SRS00 0x200 +#define SDHCI_CDNS_SRS01 0x204 +#define SDHCI_CDNS_SRS02 0x208 +#define SDHCI_CDNS_SRS03 0x20c +#define SDHCI_CDNS_SRS04 0x210 +#define SDHCI_CDNS_SRS08 0x220 +#define SDHCI_CDNS_SRS09 0x224 +#define SDHCI_CDNS_SRS09_CI BIT(16) +#define SDHCI_CDNS_SRS10 0x228 +#define SDHCI_CDNS_SRS10_VOLT_ON FIELD_PREP(GENMASK(11, 9), BIT_ONE)\ + | FIELD_PREP(BIT(8), BIT_ONE) | FIELD_PREP(BIT(0), BIT_ZERO) +#define SDHCI_CDNS_SRS10_VOLT_OFF FIELD_PREP(GENMASK(11, 9), BIT_ONE)\ + | FIELD_PREP(BIT(8), BIT_ZERO) + +#define SDHCI_CDNS_SRS11 0x22c +#define SDHCI_CDNS_SRS11_RST 0x0 +#define SDHCI_CDNS_SRS12 0x230 +#define SDHCI_CDNS_SRS13 0x234 +#define SDHCI_CDNS_SRS13_DATA 0xffffffff +#define SDHCI_CDNS_SRS14 0x238 +#define SDHCI_CDNS_SRS15 0x23c + +/* PHY register values */ +#define PHY_DQ_TIMING_REG 0x2000 +#define PHY_DQS_TIMING_REG 0x2004 +#define PHY_GATE_LPBK_CTRL_REG 0x2008 +#define PHY_DLL_MASTER_CTRL_REG 0x200C +#define PHY_DLL_SLAVE_CTRL_REG 0x2010 +#define PHY_CTRL_REG 0x2080 +#define USE_EXT_LPBK_DQS BIT(22) +#define USE_LPBK_DQS BIT(21) +#define USE_PHONY_DQS BIT(20) +#define USE_PHONY_DQS_CMD BIT(19) +#define SYNC_METHOD BIT(31) +#define SW_HALF_CYCLE_SHIFT BIT(28) +#define RD_DEL_SEL GENMASK(24, 19) +#define RD_DEL_SEL_DATA 0x34 +#define GATE_CFG_ALWAYS_ON BIT(6) +#define UNDERRUN_SUPPRESS BIT(18) +#define PARAM_DLL_BYPASS_MODE BIT(23) +#define PARAM_PHASE_DETECT_SEL GENMASK(22, 20) +#define PARAM_DLL_START_POINT GENMASK(7, 0) +#define PARAM_PHASE_DETECT_SEL_DATA 0x2 +#define PARAM_DLL_START_POINT_DATA 0x4 +#define PARAM_DLL_START_POINT_DATA_SDR50 254 + +#define READ_DQS_CMD_DELAY GENMASK(31, 24) +#define CLK_WRDQS_DELAY GENMASK(23, 16) +#define CLK_WR_DELAY GENMASK(15, 8) +#define READ_DQS_DELAY GENMASK(7, 0) +#define READ_DQS_CMD_DELAY_DATA 0x0 +#define CLK_WRDQS_DELAY_DATA 0xca +#define CLK_WR_DELAY_DATA 0xca +#define READ_DQS_DELAY_DATA 0x0 + +#define PHONY_DQS_TIMING GENMASK(9, 4) +#define PHONY_DQS_TIMING_DATA 0x0 + +#define IO_MASK_ALWAYS_ON BIT(31) +#define IO_MASK_END GENMASK(29, 27) +#define IO_MASK_START GENMASK(26, 24) +#define DATA_SELECT_OE_END GENMASK(2, 0) +/* TODO: check DV dfi_init val=0 */ +#define IO_MASK_END_DATA 0x5 +/* TODO: check DV dfi_init val=2; DDR Mode */ +#define IO_MASK_END_DATA_DDR 0x2 +#define IO_MASK_START_DATA 0x0 +#define DATA_SELECT_OE_END_DATA 0x1 + +/* rstmgr settings */ +#define COMBOPHY_RST BIT(6) +#define SDMMC_RST BIT(7) + +struct cdns_combophy_reg_pairs { + u32 reg; + u32 val; + u32 op; +}; + +struct sdhci_cdns_phy_param { + u32 addr; + u32 data; + u32 offset; +}; + +struct sdhci_cdns_phy_cfg { + const char *property; + u32 addr; + u32 offset; +}; + +struct cdns_combophy_plat { + u32 phy_type; + void __iomem *hrs_addr; + u32 nr_phy_params; + struct sdhci_cdns_phy_param *phy_params; + struct reset_ctl softreset_ctl; +}; + +struct cdns_combophy_phy { + struct udevice *dev; + void *base; +}; + +struct cdns_combophy_data { + u32 speed_mode; + struct cdns_combophy_reg_pairs *cdns_phy_regs; +}; + +static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { + { "cdns,phy-use-ext-lpbk-dqs", PHY_DQS_TIMING_REG, 22,}, + { "cdns,phy-use-lpbk-dqs", PHY_DQS_TIMING_REG, 21,}, + { "cdns,phy-use-phony-dqs", PHY_DQS_TIMING_REG, 20,}, + { "cdns,phy-use-phony-dqs-cmd", PHY_DQS_TIMING_REG, 19,}, + { "cdns,phy-io-mask-always-on", PHY_DQ_TIMING_REG, 31,}, + { "cdns,phy-io-mask-end", PHY_DQ_TIMING_REG, 27,}, + { "cdns,phy-io-mask-start", PHY_DQ_TIMING_REG, 24,}, + { "cdns,phy-data-select-oe-end", PHY_DQ_TIMING_REG, 0,}, + { "cdns,phy-sync-method", PHY_GATE_LPBK_CTRL_REG, 31,}, + { "cdns,phy-sw-half-cycle-shift", PHY_GATE_LPBK_CTRL_REG, 28,}, + { "cdns,phy-rd-del-sel", PHY_GATE_LPBK_CTRL_REG, 19,}, + { "cdns,phy-underrun-suppress", PHY_GATE_LPBK_CTRL_REG, 18,}, + { "cdns,phy-gate-cfg-always-on", PHY_GATE_LPBK_CTRL_REG, 6,}, + { "cdns,phy-param-dll-bypass-mode", PHY_DLL_MASTER_CTRL_REG, 23,}, + { "cdns,phy-param-phase-detect-sel", PHY_DLL_MASTER_CTRL_REG, 20,}, + { "cdns,phy-param-dll-start-point", PHY_DLL_MASTER_CTRL_REG, 0,}, + { "cdns,phy-read-dqs-cmd-delay", PHY_DLL_SLAVE_CTRL_REG, 24,}, + { "cdns,phy-clk-wrdqs-delay", PHY_DLL_SLAVE_CTRL_REG, 16,}, + { "cdns,phy-clk-wr-delay", PHY_DLL_SLAVE_CTRL_REG, 8,}, + { "cdns,phy-read-dqs-delay", PHY_DLL_SLAVE_CTRL_REG, 0,}, + { "cdns,phy-phony-dqs-timing", PHY_CTRL_REG, 4,}, + { "cdns,hrs09-rddata-en", SDHCI_CDNS_HRS09, 16,}, + { "cdns,hrs09-rdcmd-en", SDHCI_CDNS_HRS09, 15,}, + { "cdns,hrs09-extended-wr-mode", SDHCI_CDNS_HRS09, 3,}, + { "cdns,hrs09-extended-rd-mode", SDHCI_CDNS_HRS09, 2,}, + { "cdns,hrs10-hcsdclkadj", SDHCI_CDNS_HRS10, 16,}, + { "cdns,hrs16-wrdata1-sdclk-dly", SDHCI_CDNS_HRS16, 28,}, + { "cdns,hrs16-wrdata0-sdclk-dly", SDHCI_CDNS_HRS16, 24,}, + { "cdns,hrs16-wrcmd1-sdclk-dly", SDHCI_CDNS_HRS16, 20,}, + { "cdns,hrs16-wrcmd0-sdclk-dly", SDHCI_CDNS_HRS16, 16,}, + { "cdns,hrs16-wrdata1-dly", SDHCI_CDNS_HRS16, 12,}, + { "cdns,hrs16-wrdata0-dly", SDHCI_CDNS_HRS16, 8,}, + { "cdns,hrs16-wrcmd1-dly", SDHCI_CDNS_HRS16, 4,}, + { "cdns,hrs16-wrcmd0-dly", SDHCI_CDNS_HRS16, 0,}, + { "cdns,hrs07-rw-compensate", SDHCI_CDNS_HRS07, 16,}, + { "cdns,hrs07-idelay-val", SDHCI_CDNS_HRS07, 0,} +}; + +static u32 sdhci_cdns_phy_param_count(struct cdns_combophy_plat *plat, + const void *fdt, int nodeoffset) +{ + const fdt32_t *prop; + int i; + u32 count = 0; + + for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) { + prop = fdt_getprop(fdt, nodeoffset, + sdhci_cdns_phy_cfgs[i].property, NULL); + if (!prop) + continue; + + count++; + } + + debug("%s: count:%d\n", __func__, count); + return count; +} + +static void sdhci_cdns_phy_param_parse(struct cdns_combophy_plat *plat, + const void *fdt, int nodeoffset) +{ + struct sdhci_cdns_phy_param *p = plat->phy_params; + const fdt32_t *prop; + int i; + + for (i = 0; i < plat->nr_phy_params; i++) { + prop = fdt_getprop(fdt, nodeoffset, + sdhci_cdns_phy_cfgs[i].property, NULL); + if (!prop) + continue; + + p->addr = sdhci_cdns_phy_cfgs[i].addr; + p->data = fdt32_to_cpu(*prop); + p->offset = sdhci_cdns_phy_cfgs[i].offset; + debug("%s: p->addr: %02x, p->data: %02x, p->offset: %02x\n", + __func__, p->addr, p->data, p->offset); + p++; + } +} + +static u32 sdhci_cdns_dfi_phy_val(struct cdns_combophy_plat *plat, u32 reg) +{ + int i; + u32 tmp = 0; + + for (i = 0; i < plat->nr_phy_params; i++) { + if (plat->phy_params[i].addr == reg) { + tmp |= plat->phy_params[i].data << plat->phy_params[i].offset; + debug("%s: p->addr: %02x, p->data: %02x, p->offset: %02x\n", + __func__, plat->phy_params[i].addr, plat->phy_params[i].data, + plat->phy_params[i].offset); + } + } + + debug("%s: reg value:%08x\n", __func__, tmp); + return tmp; +} + +static int sdhci_cdns_write_phy_reg_mask(u32 addr, u32 data, u32 mask, + void __iomem *hrs_addr) +{ + void __iomem *reg = hrs_addr + SDHCI_CDNS_HRS04; + u32 tmp; + + tmp = addr; + + /* get PHY address */ + writel(tmp, reg); + debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS04)); + + /* read current PHY register value, before write */ + reg = hrs_addr + SDHCI_CDNS_HRS05; + tmp = readl(reg); + debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS05)); + tmp &= ~mask; + tmp |= data; + + /* write operation */ + writel(tmp, reg); + debug("%s: register = 0x%08x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS05)); + + return 0; +} + +static u32 sdhci_cdns_read_phy_reg(u32 addr, void __iomem *hrs_addr) +{ + void __iomem *reg = hrs_addr + SDHCI_CDNS_HRS04; + u32 tmp; + + tmp = addr; + + /* get PHY address */ + writel(tmp, reg); + debug("%s: register = 0x%08x\n", __func__, readl(reg)); + + /* read current PHY register value, before write */ + reg = hrs_addr + SDHCI_CDNS_HRS05; + tmp = readl(reg); + debug("%s: register = 0x%08x\n", __func__, readl(reg)); + + return tmp; +} + +/* initialize clock */ +static void sdhci_set_clk(u32 freq_khz, void __iomem *hrs_addr) +{ + void __iomem *reg = hrs_addr + SDHCI_CDNS_SRS11; + + /* calculate the value to set */ + u32 sdclkfs = (SD_HOST_CLK / 2000) / freq_khz; + u32 dtcvval = 0xe; + + /* disable SDCE */ + writel(0, reg); + debug("Disable SDCE\n"); + + /* Enable ICE (internal clock enable) */ + writel(dtcvval << 16 | sdclkfs << 8 | 1 << 0, reg); + debug("Enable ICE: SRS11: %08x\n", readl(reg)); + + reg = hrs_addr + SDHCI_CDNS_SRS11; + /* Wait for ICS (internal clock stable) */ + /* polling for ICS = 1 */ + wait_for_bit_le32(reg, BIT(1), true, 10, false); + + reg = hrs_addr + SDHCI_CDNS_HRS09; + /* Enable DLL reset */ + writel(readl(reg) & ~0x00000001, reg); + debug("Enable DLL reset\n"); + + /* Set extended_wr_mode */ + writel((readl(reg) & 0xFFFFFFF7) | + ((sdclkfs > 0 ? 1 << 3 : 0 << 3)), reg); + debug("Set extended_wr_mode\n"); + + /* Release DLL reset */ + writel(readl(reg) | BIT(0), reg); + writel(readl(reg) | 3 << 15, reg); + debug("Release DLL reset\n"); + + /* polling for phy_init = 1 */ + wait_for_bit_le32(reg, BIT(1), true, 10, false); + + debug("PHY init complete\n"); + + reg = hrs_addr + SDHCI_CDNS_SRS11; + writel(dtcvval << 16 | sdclkfs << 8 | 1 << 0 | 1 << 2, reg); + debug("SD clock enabled\n"); +} + +static int sdhci_cdns_detect_card(void __iomem *hrs_addr) +{ + u32 tmp; + void __iomem *reg = hrs_addr + SDHCI_CDNS_SRS09; + + /* step 1, polling for sdcard */ + /* polling for SRS9.CI = 1 */ + wait_for_bit_le32(reg, SDHCI_CDNS_SRS09_CI, true, 10, false); + + debug("SDcard detected\n"); + + debug("ON voltage\n"); + tmp = SDHCI_CDNS_SRS10_VOLT_ON; + reg = hrs_addr + SDHCI_CDNS_SRS10; + writel(tmp, reg); + + debug("OFF Voltage\n"); + tmp = SDHCI_CDNS_SRS10_VOLT_OFF; + writel(tmp, reg); + + debug("ON voltage\n"); + tmp = SDHCI_CDNS_SRS10_VOLT_ON; + reg = hrs_addr + SDHCI_CDNS_SRS10; + writel(tmp, reg); + + /* sdhci_set_clk - 100000 */ + debug("SD clock\n"); + sdhci_set_clk(100000, hrs_addr); + + /* set SRS13 */ + tmp = SDHCI_CDNS_SRS13_DATA; + reg = hrs_addr + SDHCI_CDNS_SRS13; + writel(tmp, reg); + debug("SRS13: %08x\n", readl(reg)); + + return 0; +} + +static int sdhci_cdns_combophy_phy_prog(void __iomem *hrs_addr) +{ + u32 ret, tmp; + u32 mask = 0x0; + + /* step 1, switch on SW_RESET */ + tmp = SDHCI_CDNS_HRS00_SWR; + writel(tmp, hrs_addr); + /* polling for HRS0.0 (SWR) = 0 */ + wait_for_bit_le32(hrs_addr, SDHCI_CDNS_HRS00_SWR, false, 10, false); + debug("SW reset\n"); + + /* switch on DLL_RESET */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09); + tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ZERO); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + debug("PHY_SW_RESET: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS09)); + + /* program PHY_DQS_TIMING_REG */ + tmp = FIELD_PREP(USE_EXT_LPBK_DQS, BIT_ONE) | + FIELD_PREP(USE_LPBK_DQS, BIT_ONE) | + FIELD_PREP(USE_PHONY_DQS, BIT_ONE) | + FIELD_PREP(USE_PHONY_DQS_CMD, BIT_ONE); + ret = sdhci_cdns_write_phy_reg_mask(PHY_DQS_TIMING_REG, tmp, tmp, hrs_addr); + + /* program PHY_GATE_LPBK_CTRL_REG */ + tmp = FIELD_PREP(SYNC_METHOD, BIT_ONE) | + FIELD_PREP(SW_HALF_CYCLE_SHIFT, BIT_ZERO) | + FIELD_PREP(RD_DEL_SEL, RD_DEL_SEL_DATA) | + FIELD_PREP(UNDERRUN_SUPPRESS, BIT_ONE) | + FIELD_PREP(GATE_CFG_ALWAYS_ON, BIT_ONE); + mask = SYNC_METHOD | SW_HALF_CYCLE_SHIFT | RD_DEL_SEL | UNDERRUN_SUPPRESS | + GATE_CFG_ALWAYS_ON; + ret = sdhci_cdns_write_phy_reg_mask(PHY_GATE_LPBK_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_DLL_MASTER_CTRL_REG */ + tmp = FIELD_PREP(PARAM_DLL_BYPASS_MODE, BIT_ONE) | + FIELD_PREP(PARAM_PHASE_DETECT_SEL, PARAM_PHASE_DETECT_SEL_DATA) | + FIELD_PREP(PARAM_DLL_START_POINT, PARAM_DLL_START_POINT_DATA); + mask = PARAM_DLL_BYPASS_MODE | PARAM_PHASE_DETECT_SEL | + PARAM_DLL_START_POINT; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_MASTER_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_DLL_SLAVE_CTRL_REG */ + tmp = FIELD_PREP(READ_DQS_CMD_DELAY, READ_DQS_CMD_DELAY_DATA) | + FIELD_PREP(CLK_WRDQS_DELAY, CLK_WRDQS_DELAY_DATA) | + FIELD_PREP(CLK_WR_DELAY, CLK_WR_DELAY_DATA) | + FIELD_PREP(READ_DQS_DELAY, READ_DQS_DELAY_DATA); + mask = READ_DQS_CMD_DELAY | CLK_WRDQS_DELAY | CLK_WR_DELAY | READ_DQS_DELAY; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_SLAVE_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_CTRL_REG */ + tmp = FIELD_PREP(PHONY_DQS_TIMING, PHONY_DQS_TIMING_DATA); + mask = PHONY_DQS_TIMING; + ret = sdhci_cdns_write_phy_reg_mask(PHY_CTRL_REG, tmp, mask, hrs_addr); + + /* switch off DLL_RESET */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09); + tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ONE); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + + /* polling for PHY_INIT_COMPLETE bit */ + wait_for_bit_le32(hrs_addr + SDHCI_CDNS_HRS09, + SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE, true, 10, false); + + /* program PHY_DQ_TIMING_REG */ + tmp = sdhci_cdns_read_phy_reg(PHY_DQ_TIMING_REG, hrs_addr) & 0x07FFFF8; + tmp |= FIELD_PREP(IO_MASK_ALWAYS_ON, BIT_ZERO) | + FIELD_PREP(IO_MASK_END, IO_MASK_END_DATA) | + FIELD_PREP(IO_MASK_START, IO_MASK_START_DATA) | + FIELD_PREP(DATA_SELECT_OE_END, DATA_SELECT_OE_END_DATA); + mask = IO_MASK_ALWAYS_ON | IO_MASK_END | IO_MASK_START | DATA_SELECT_OE_END; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DQ_TIMING_REG, tmp, mask, hrs_addr); + + /* program HRS09, register 42 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09) & 0xFFFE7FF3; + tmp &= ~(SDHCI_CDNS_HRS09_RDDATA_EN | + SDHCI_CDNS_HRS09_RDCMD_EN | + SDHCI_CDNS_HRS09_EXTENDED_WR_MODE | + SDHCI_CDNS_HRS09_EXTENDED_RD_MODE); + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_RDDATA_EN, BIT_ONE) | + FIELD_PREP(SDHCI_CDNS_HRS09_RDCMD_EN, BIT_ONE) | + FIELD_PREP(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE, BIT_ONE) | + FIELD_PREP(SDHCI_CDNS_HRS09_EXTENDED_RD_MODE, BIT_ONE); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS09)); + + /* program HRS10, register 43 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS10) & 0xFFF0FFFF; + tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ, + SDHCI_CDNS_HRS10_HCSDCLKADJ_DATA); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS10); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS10)); + + /* program HRS16, register 48 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS16); + tmp &= ~(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY | + SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY | + SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY | + SDHCI_CDNS_HRS16_WRDATA1_DLY | + SDHCI_CDNS_HRS16_WRDATA0_DLY | + SDHCI_CDNS_HRS16_WRCMD1_DLY | + SDHCI_CDNS_HRS16_WRCMD0_DLY); + tmp |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY, + SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY, + SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY, + SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY, + SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY, + SDHCI_CDNS_HRS16_WRDATA1_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY, + SDHCI_CDNS_HRS16_WRDATA0_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY, + SDHCI_CDNS_HRS16_WRCMD1_DLY_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY, + SDHCI_CDNS_HRS16_WRCMD0_DLY_DATA); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS16); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS16)); + + /* program HRS07, register 40 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS07); + tmp &= ~(SDHCI_CDNS_HRS07_RW_COMPENSATE | + SDHCI_CDNS_HRS07_IDELAY_VAL); + tmp |= FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE, + SDHCI_CDNS_HRS07_RW_COMPENSATE_DATA) | + FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL, + SDHCI_CDNS_HRS07_IDELAY_VAL_DATA); + writel(tmp, hrs_addr + SDHCI_CDNS_HRS07); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS07)); + /* end of combophy init */ + + /* initialize sdcard/eMMC & set clock*/ + ret = sdhci_cdns_detect_card(hrs_addr); + debug("eMMC Mode: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS06)); + + return 0; +} + +static int cdns_combophy_phy_init(struct phy *gphy) +{ + u32 tmp; + int ret; + struct cdns_combophy_plat *plat = dev_get_plat(gphy->dev); + +#if (IS_ENABLED(CONFIG_SPL_BUILD)) + /* assert & deassert softreset */ + ret = reset_assert(&plat->softreset_ctl); + if (ret < 0) { + pr_err("COMBOPHY soft reset deassert failed: %d", ret); + return ret; + } + + ret = reset_deassert(&plat->softreset_ctl); + if (ret < 0) { + pr_err("COMBOPHY soft reset deassert failed: %d", ret); + return ret; + } +#endif + + if (plat->phy_type == PHY_TYPE_SDMMC) { + tmp = SYSMGR_SOC64_COMBOPHY_DFISEL_SDMMC; + +#if (IS_ENABLED(CONFIG_SPL_BUILD)) +#if (IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5_EMU)) + /* OCRAM FW - allow non secure sdmmc */ + writel(NON_SECURE_ACCESS, OCRAM_SECURE_REGION1_REG); +#endif + /* configure DFI_SEL for SDMMC */ + writel(tmp, socfpga_get_sysmgr_addr() + SYSMGR_SOC64_COMBOPHY_DFISEL); + debug("DFISEL: %08x\nSDMMC_USEFPGA: %08x\n", + readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_COMBOPHY_DFISEL), + readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_SDMMC_USEFPGA)); + debug("clkmgr.mainpllgrp.en: 0x%08x\nclkmgr.mainpllgrp.ens: 0x%08x\n", + readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_EN), + readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_ENS)); + debug("clkmgr.perpllgrp.en: 0x%08x\nclkmgr.perpllgrp.ens: 0x%08x\n", + readl(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN), + readl(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_ENS)); + + /* configure default base clkmgr clock - 200MHz */ + writel((readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV) + & 0xfffcffff) | + (CLKMGR_NOCDIV_SOFTPHY_DIV_ONE << CLKMGR_NOCDIV_SOFTPHY_OFFSET), + socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV); + + debug("clkmgr.nocdiv: 0x%08x\n", + readl(socfpga_get_clkmgr_addr() + CLKMGR_MAINPLL_NOCDIV)); + + /* enable DDR secure zone access for SDMMC */ + writel(SECURE_TRANS_SET, SECURE_TRANS_REG); +#endif + + /* TODO: add speed-mode checking in device tree */ + ret = sdhci_cdns_combophy_phy_prog(plat->hrs_addr); + + } else if (plat->phy_type == PHY_TYPE_NAND) { + /* configure DFI_SEL for NAND */ + } else { + return -EINVAL; + } + + return 0; +} + +static int cdns_combophy_phy_on(struct phy *gphy) +{ + return 0; +} + +static int cdns_combophy_phy_configure(struct phy *gphy, void *params) +{ + u32 ret, tmp; + u32 mask = 0x0; + struct cdns_combophy_plat *plat = dev_get_plat(gphy->dev); + void __iomem *hrs_addr = plat->hrs_addr; + + /* switch on DLL_RESET */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09); + tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ZERO); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + debug("PHY_SW_RESET: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS09)); + + /* program PHY_DQS_TIMING_REG */ + tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DQS_TIMING_REG); + ret = sdhci_cdns_write_phy_reg_mask(PHY_DQS_TIMING_REG, tmp, tmp, hrs_addr); + + /* program PHY_GATE_LPBK_CTRL_REG */ + tmp = sdhci_cdns_dfi_phy_val(plat, PHY_GATE_LPBK_CTRL_REG); + mask = SYNC_METHOD | SW_HALF_CYCLE_SHIFT | RD_DEL_SEL | UNDERRUN_SUPPRESS | + GATE_CFG_ALWAYS_ON; + ret = sdhci_cdns_write_phy_reg_mask(PHY_GATE_LPBK_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_DLL_MASTER_CTRL_REG */ + tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DLL_MASTER_CTRL_REG); + mask = PARAM_DLL_BYPASS_MODE | PARAM_PHASE_DETECT_SEL | + PARAM_DLL_START_POINT; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_MASTER_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_DLL_SLAVE_CTRL_REG */ + tmp = sdhci_cdns_dfi_phy_val(plat, PHY_DLL_SLAVE_CTRL_REG); + mask = READ_DQS_CMD_DELAY | CLK_WRDQS_DELAY | CLK_WR_DELAY | READ_DQS_DELAY; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DLL_SLAVE_CTRL_REG, tmp, mask, + hrs_addr); + + /* program PHY_CTRL_REG */ + debug("%s: Skip PHY_CTRL_REG - PHONY_DQS_TIMING field\n", __func__); + + /* switch off DLL_RESET */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09); + tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, BIT_ONE); + + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + + /* polling for PHY_INIT_COMPLETE bit */ + wait_for_bit_le32(hrs_addr + SDHCI_CDNS_HRS09, + SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE, true, 10, false); + + /* program PHY_DQ_TIMING_REG */ + tmp = sdhci_cdns_read_phy_reg(PHY_DQ_TIMING_REG, hrs_addr) & 0x07FFFF8; + tmp |= sdhci_cdns_dfi_phy_val(plat, PHY_DQ_TIMING_REG); + mask = IO_MASK_ALWAYS_ON | IO_MASK_END | IO_MASK_START | DATA_SELECT_OE_END; + ret = sdhci_cdns_write_phy_reg_mask(PHY_DQ_TIMING_REG, tmp, mask, hrs_addr); + + /* program HRS09, register 42 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS09) & 0xFFFE7FF3; + tmp |= sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS09); + writel(tmp, hrs_addr + SDHCI_CDNS_HRS09); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS09)); + + /* program HRS10, register 43 */ + tmp = readl(hrs_addr + SDHCI_CDNS_HRS10) & 0xFFF0FFFF; + tmp |= sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS10); + writel(tmp, hrs_addr + SDHCI_CDNS_HRS10); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS10)); + + /* program HRS16, register 48 */ + tmp = sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS16); + writel(tmp, hrs_addr + SDHCI_CDNS_HRS16); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS16)); + + /* program HRS07, register 40 */ + tmp = sdhci_cdns_dfi_phy_val(plat, SDHCI_CDNS_HRS07); + writel(tmp, hrs_addr + SDHCI_CDNS_HRS07); + debug("%s: register = 0x%x\n", __func__, readl(hrs_addr + + SDHCI_CDNS_HRS07)); + /* end of combophy init */ + + /* initialize sdcard/eMMC & set clock*/ + ret = sdhci_cdns_detect_card(hrs_addr); + debug("eMMC Mode: 0x%08x\n", readl(hrs_addr + SDHCI_CDNS_HRS06)); + + return 0; +} + +static int cdns_combophy_phy_off(struct phy *gphy) +{ + return 0; +} + +static int cdns_combophy_phy_reset(struct phy *gphy) +{ + return 0; +}; + +static const struct phy_ops ops = { + .configure = cdns_combophy_phy_configure, + .init = cdns_combophy_phy_init, + .power_on = cdns_combophy_phy_on, + .power_off = cdns_combophy_phy_off, + .reset = cdns_combophy_phy_reset, +}; + +static int cdns_combophy_phy_probe(struct udevice *dev) +{ + struct cdns_combophy_plat *plat = dev_get_plat(dev); + u32 nr_phy_params; + int ret = 0; + + plat->phy_type = dev_read_u32_default(dev, "phy-type", 0); + plat->hrs_addr = dev_remap_addr_index(dev, 0); + + /* get softreset reset */ + ret = reset_get_by_name(dev, "reset", &plat->softreset_ctl); + if (ret) + pr_err("can't get soft reset for %s (%d)", dev->name, ret); + + nr_phy_params = sdhci_cdns_phy_param_count(plat, gd->fdt_blob, + dev_of_offset(dev)); + if (!nr_phy_params) + return ret; + + plat->phy_params = kcalloc(nr_phy_params, sizeof(*plat->phy_params), + GFP_KERNEL); + if (!plat->phy_params) + return -ENOMEM; + + plat->nr_phy_params = nr_phy_params; + sdhci_cdns_phy_param_parse(plat, gd->fdt_blob, dev_of_offset(dev)); + + debug("%s: ComboPHY probed\n", __func__); + + return ret; +} + +static int cdns_combophy_phy_remove(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id cdns_combophy_id_table[] = { + { .compatible = "cdns,combophy" }, + {} +}; + +U_BOOT_DRIVER(combophy_phy_provider) = { + .name = "combophy", + .id = UCLASS_PHY, + .of_match = cdns_combophy_id_table, + .probe = cdns_combophy_phy_probe, + .remove = cdns_combophy_phy_remove, + .ops = &ops, + .priv_auto = sizeof(struct cdns_combophy_phy), + .plat_auto = sizeof(struct cdns_combophy_plat), +};