From patchwork Wed Jan 4 12:34:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaehoon Chung X-Patchwork-Id: 9496611 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A92CC606DD for ; Wed, 4 Jan 2017 12:37:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9994B27F46 for ; Wed, 4 Jan 2017 12:37:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8E27B27F85; Wed, 4 Jan 2017 12:37:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D08A27F46 for ; Wed, 4 Jan 2017 12:37:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S967071AbdADMf6 (ORCPT ); Wed, 4 Jan 2017 07:35:58 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:36322 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S966638AbdADMf3 (ORCPT ); Wed, 4 Jan 2017 07:35:29 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OJ902SOD9LO2L60@mailout1.samsung.com>; Wed, 04 Jan 2017 21:34:36 +0900 (KST) Received: from epsmges1p2.samsung.com (unknown [182.195.40.65]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20170104123436epcas1p4e98a3f3917bca7f5c9fae56f4406842d~WkaelP-Z10984209842epcas1p4-; Wed, 4 Jan 2017 12:34:36 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 79.03.12664.CDBEC685; Wed, 4 Jan 2017 21:34:36 +0900 (KST) Received: from epcpsbgm1new.samsung.com (u26.gpu120.samsung.co.kr [203.254.230.26]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20170104123436epcas1p1a729583c3c2307d8539a186f1050ea98~WkaeWAoOY0860508605epcas1p1F; Wed, 4 Jan 2017 12:34:36 +0000 (GMT) X-AuditID: b6c32a36-f790e6d000003178-8d-586cebdcaaed Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1new.samsung.com (EPCPMTA) with SMTP id BA.F8.28252.CDBEC685; Wed, 4 Jan 2017 21:34:36 +0900 (KST) Received: from localhost.localdomain ([10.113.62.216]) by mmp2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OJ900IEZ9LNGDB0@mmp2.samsung.com>; Wed, 04 Jan 2017 21:34:36 +0900 (KST) From: Jaehoon Chung To: linux-pci@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-samsung-soc@vger.kernel.org, bhelgaas@google.com, robh+dt@kernel.org, mark.rutland@arm.com, kgene@kernel.org, krzk@kernel.org, kishon@ti.com, jingoohan1@gmail.com, vivek.gautam@codeaurora.org, pankaj.dubey@samsung.com, alim.akhtar@samsung.com, cpgs@samsung.com, Jaehoon Chung Subject: [PATCH V2 2/5] phy: phy-exynos-pcie: Add support for Exynos PCIe phy Date: Wed, 04 Jan 2017 21:34:32 +0900 Message-id: <20170104123435.30740-3-jh80.chung@samsung.com> X-Mailer: git-send-email 2.10.2 In-reply-to: <20170104123435.30740-1-jh80.chung@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrBKsWRmVeSWpSXmKPExsWy7bCmvu6d1zkRBgsXSlk8mLeNzWJJU4bF y0OaFvOPnGO1uPGrjdVixZeZ7Bb9j18zW1x42sNmcf78BnaLy7vmsFmcnXeczWLG+X1MFkuv X2SyWLT1C7tF694j7BYnfu5gdhDwWDNvDaPH5b5eJo+ds+6yeyzYVOqxaVUnm0ffllWMHsdv bGfy+LxJLoAjKtUmIzUxJbVIITUvOT8lMy/dVsk7ON453tTMwFDX0NLCXEkhLzE31VbJxSdA 1y0zB+h+JYWyxJxSoFBAYnGxkr6dTVF+aUmqQkZ+cYmtUrShoZGeoYG5npGRkZ6JcayVkSlQ SUJqRndvTMHG2IonrR9ZGhiXB3QxcnJICJhILDi4gg3CFpO4cG89kM3FISSwg1GibflnVgin nUmiee86dpiObyvbWSAScxglXu8+wwTh/GCUuLezH2wWm4COxPZvx5lAbBEBWYmPl/eAzWUW +MUkcWDfB7CEsIC/xLlPG1hBbBYBVYllf1cCxTk4eAWsJZrfqkBsk5dYeP4IWDmngI3Ev6Ub wJZJCBxjlzg6sYUdpF4CaMGmA8wQ9S4S1yZsZISwhSVeHd8CdbW0xKp/t6B6uxkl/n3ZyAbh 9DBK3Nq6mgmiylji/oN7YJOYBfgk3n3tYYVYwCvR0SYEYXpIzP2eA2E6Smx6zgfxez/QlMUP mCcwyixgZFjFKJZaUJybnlpsWGCkV5yYW1yal66XnJ+7iRGc6rTMdjAuOudziFGAg1GJh3fF ouwIIdbEsuLK3EOMEhzMSiK8317kRAjxpiRWVqUW5ccXleakFh9iNAWG0kRmKdHkfGAaziuJ NzQxMzQxMjE0NDcyMFIS513caB0hJJCeWJKanZpakFoE08fEwSnVwNi5vWvVg1sW0d6S7ySX x+y/U1rxsvSk73a17/9qk+Lnb5r5nk3t7C9/dQ3jm878Zm/b9fZzfzbc2nCigivoyKVVrYHh VZ1l4Q8284qvXbpLZW0UY1pMy+u7Pzr2JMUm7fZjC159LyCp5BGDzQ2GtD+Vc9quTp7bXChl nvNY7MqZAKsbfUt9ApVYijMSDbWYi4oTAXDin6OLAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrMIsWRmVeSWpSXmKPExsVy+t9jQd07r3MiDJbM5rZ4MG8bm8WSpgyL l4c0LeYfOcdqceNXG6vFii8z2S36H79mtrjwtIfN4vz5DewWl3fNYbM4O+84m8WM8/uYLJZe v8hksWjrF3aL1r1H2C1O/NzB7CDgsWbeGkaPy329TB47Z91l91iwqdRj06pONo++LasYPY7f 2M7k8XmTXABHlJtNRmpiSmqRQmpecn5KZl66rVJoiJuuhZJCXmJuqq1ShK5vSJCSQlliTimQ Z2SABhycA9yDlfTtEtwyuntjCjbGVjxp/cjSwLg8oIuRk0NCwETi28p2FghbTOLCvfVsXYxc HEICsxglelZfYAJJCAn8YJTo+OIHYrMJ6Ehs/3YcLC4iICvx8fIeNhCbWeAXk8TSfaYgtrCA r8TVeY/A4iwCqhLL/q4Equfg4BWwlmh+qwKxS15i4fkjYGM4BWwk/i3dALXKWqLv5ASmCYy8 CxgZVjFKpBYkFxQnpeca5qWW6xUn5haX5qXrJefnbmIER9AzqR2MB3e5H2IU4GBU4uEVuJ8T IcSaWFZcmXuIUYKDWUmE99sLoBBvSmJlVWpRfnxRaU5q8SFGU6C7JjJLiSbnA6M7ryTe0MTc xNzYwMLc0tLESEmct3H2s3AhgfTEktTs1NSC1CKYPiYOTqkGRveSFCfxh9G3mwqOcT68V36r Ym7Rv12cxcv7Zryb0qryOlpxbljVkX/Pos6dVnlhOuGCuMiBRQdvLWJoTAnUu8J55FtjxdnH 7oy3ajOOf/aJ1mtcby9ip92Sox2Sdt/x+JsZhxdM3/LN+Y3fgoOLrOdurS7oqRLe7yTUwfJU pfr9Oa/fJvte5SmxFGckGmoxFxUnAgBVy1p1tgIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170104123436epcas1p1a729583c3c2307d8539a186f1050ea98 X-Msg-Generator: CA X-Sender-IP: 203.254.230.26 X-Local-Sender: =?UTF-8?B?7KCV7J6s7ZuIG1RpemVuIFBsYXRmb3JtIExhYihTL1fshLw=?= =?UTF-8?B?7YSwKRvsgrzshLHsoITsnpAbUzUo7LGF7J6EKS/ssYXsnoQ=?= X-Global-Sender: =?UTF-8?B?SmFlaG9vbiBDaHVuZxtUaXplbiBQbGF0Zm9ybSBMYWIuG1Nh?= =?UTF-8?B?bXN1bmcgRWxlY3Ryb25pY3MbUzUvU2VuaW9yIEVuZ2luZWVy?= X-Sender-Code: =?UTF-8?B?QzEwG1NUQUYbQzEwVjgxMTE=?= CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-HopCount: 7 X-CMS-RootMailID: 20170104123436epcas1p1a729583c3c2307d8539a186f1050ea98 X-RootMTR: 20170104123436epcas1p1a729583c3c2307d8539a186f1050ea98 References: <20170104123435.30740-1-jh80.chung@samsung.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch supports to use Generic Phy framework for Exynos PCIe phy. When Exynos that supported the pcie want to use the PCIe, it needs to control the phy resgister. But it should be more complex to control in their own PCIe device drivers. Currently, there is an exynos5440 case to support the pcie. So this driver is based on Exynos5440 PCIe. In future, will support the Other exynos SoCs likes exynos5433, exynos7. Signed-off-by: Jaehoon Chung Acked-by: Krzysztof Kozlowski Reviewed-by: Jingoo Han Reviewed-by: Pankaj Dubey --- Changelog on V2: - Not include the codes relevant to pci-exynos. - Remove the getting child node. drivers/phy/Kconfig | 9 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-exynos-pcie.c | 280 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 drivers/phy/phy-exynos-pcie.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e8eb7f2..2dddef4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -331,6 +331,15 @@ config PHY_EXYNOS5_USBDRD This driver provides PHY interface for USB 3.0 DRD controller present on Exynos5 SoC series. +config PHY_EXYNOS_PCIE + bool "Exynos PCIe PHY driver" + depends on ARCH_EXYNOS && OF + depends on PCI_EXYNOS + select GENERIC_PHY + help + Enable PCIe PHY support for Exynos SoC series. + This driver provides PHY interface for Exynos PCIe controller. + config PHY_PISTACHIO_USB tristate "IMG Pistachio USB2.0 PHY driver" depends on MACH_PISTACHIO diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 65eb2f4..081aeb4 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -37,6 +37,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o +obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o diff --git a/drivers/phy/phy-exynos-pcie.c b/drivers/phy/phy-exynos-pcie.c new file mode 100644 index 0000000..b57f49b --- /dev/null +++ b/drivers/phy/phy-exynos-pcie.c @@ -0,0 +1,280 @@ +/* + * Samsung EXYNOS SoC series PCIe PHY driver + * + * Phy provider for PCIe controller on Exynos SoC series + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * Jaehoon Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PCIe Purple registers */ +#define PCIE_PHY_GLOBAL_RESET 0x000 +#define PCIE_PHY_COMMON_RESET 0x004 +#define PCIE_PHY_CMN_REG 0x008 +#define PCIE_PHY_MAC_RESET 0x00c +#define PCIE_PHY_PLL_LOCKED 0x010 +#define PCIE_PHY_TRSVREG_RESET 0x020 +#define PCIE_PHY_TRSV_RESET 0x024 + +/* PCIe PHY registers */ +#define PCIE_PHY_IMPEDANCE 0x004 +#define PCIE_PHY_PLL_DIV_0 0x008 +#define PCIE_PHY_PLL_BIAS 0x00c +#define PCIE_PHY_DCC_FEEDBACK 0x014 +#define PCIE_PHY_PLL_DIV_1 0x05c +#define PCIE_PHY_COMMON_POWER 0x064 +#define PCIE_PHY_COMMON_PD_CMN BIT(3) +#define PCIE_PHY_TRSV0_EMP_LVL 0x084 +#define PCIE_PHY_TRSV0_DRV_LVL 0x088 +#define PCIE_PHY_TRSV0_RXCDR 0x0ac +#define PCIE_PHY_TRSV0_POWER 0x0c4 +#define PCIE_PHY_TRSV0_PD_TSV BIT(7) +#define PCIE_PHY_TRSV0_LVCC 0x0dc +#define PCIE_PHY_TRSV1_EMP_LVL 0x144 +#define PCIE_PHY_TRSV1_RXCDR 0x16c +#define PCIE_PHY_TRSV1_POWER 0x184 +#define PCIE_PHY_TRSV1_PD_TSV BIT(7) +#define PCIE_PHY_TRSV1_LVCC 0x19c +#define PCIE_PHY_TRSV2_EMP_LVL 0x204 +#define PCIE_PHY_TRSV2_RXCDR 0x22c +#define PCIE_PHY_TRSV2_POWER 0x244 +#define PCIE_PHY_TRSV2_PD_TSV BIT(7) +#define PCIE_PHY_TRSV2_LVCC 0x25c +#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 +#define PCIE_PHY_TRSV3_RXCDR 0x2ec +#define PCIE_PHY_TRSV3_POWER 0x304 +#define PCIE_PHY_TRSV3_PD_TSV BIT(7) +#define PCIE_PHY_TRSV3_LVCC 0x31c + +struct exynos_pcie_phy_data { + struct phy_ops *ops; +}; + +/* For Exynos pcie phy */ +struct exynos_pcie_phy { + const struct exynos_pcie_phy_data *drv_data; + void __iomem *phy_base; + void __iomem *blk_base; /* For exynos5440 */ +}; + +static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) +{ + writel(val, base + offset); +} + +static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +/* For Exynos5440 specific functions */ +static int exynos5440_pcie_phy_init(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + + /* DCC feedback control off */ + exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); + + /* set TX/RX impedance */ + exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); + + /* set 50Mhz PHY clock */ + exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); + exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); + + /* set TX Differential output for lane 0 */ + exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); + + /* set TX Pre-emphasis Level Control for lane 0 to minimum */ + exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); + + /* set RX clock and data recovery bandwidth */ + exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); + + /* change TX Pre-emphasis Level Control for lanes */ + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); + + /* set LVCC */ + exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); + + /* pulse for common reset */ + exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET); + udelay(500); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); + + return 0; +} + +static int exynos5440_pcie_phy_power_on(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + u32 val; + + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); + val &= ~PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); + val &= ~PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); + val &= ~PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); + val &= ~PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); + val &= ~PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); + + return 0; +} + +static int exynos5440_pcie_phy_power_off(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + u32 val; + + while (exynos_pcie_phy_readl(ep->phy_base, + PCIE_PHY_PLL_LOCKED) == 0) { + val = exynos_pcie_phy_readl(ep->blk_base, + PCIE_PHY_PLL_LOCKED); + dev_info(&phy->dev, "PLL Locked: 0x%x\n", val); + } + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); + val |= PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); + val |= PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); + val |= PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); + val |= PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); + val |= PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); + + return 0; +} + +static int exynos5440_pcie_phy_reset(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET); + exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET); + + return 0; +} + +static struct phy_ops exynos5440_phy_ops = { + .init = exynos5440_pcie_phy_init, + .power_on = exynos5440_pcie_phy_power_on, + .power_off = exynos5440_pcie_phy_power_off, + .reset = exynos5440_pcie_phy_reset, +}; + +static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = { + .ops = &exynos5440_phy_ops, +}; + +static const struct of_device_id exynos_pcie_phy_match[] = { + { + .compatible = "samsung,exynos5440-pcie-phy", + .data = &exynos5440_pcie_phy_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_pcie_phy_match); + +static int exynos_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_pcie_phy *exynos_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct resource *res; + const struct exynos_pcie_phy_data *drv_data; + + drv_data = of_device_get_match_data(dev); + if (!drv_data) + return -ENODEV; + + exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL); + if (!exynos_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + exynos_phy->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(exynos_phy->phy_base)) + return PTR_ERR(exynos_phy->phy_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + exynos_phy->blk_base = devm_ioremap_resource(dev, res); + if (IS_ERR(exynos_phy->phy_base)) + return PTR_ERR(exynos_phy->phy_base); + + exynos_phy->drv_data = drv_data; + + generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, exynos_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver exynos_pcie_phy_driver = { + .probe = exynos_pcie_phy_probe, + .driver = { + .of_match_table = exynos_pcie_phy_match, + .name = "exynos_pcie_phy", + } +}; +module_platform_driver(exynos_pcie_phy_driver);