Add support Axis, ARTPEC-8 SoC. ARTPEC-8 is the SoC platform of Axis Communications. This is based on arm64 and support GEN4 & 2lane. This driver provides PHY interface for ARTPEC-8 SoC PCIe controller, based on Samsung PCIe PHY IP. Signed-off-by: Wangseok Lee <wangseok.lee@xxxxxxxxxxx> --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/artpec/Kconfig | 9 + drivers/phy/artpec/Makefile | 2 + drivers/phy/artpec/phy-artpec8-pcie.c | 879 ++++++++++++++++++++++++++++++++++ 5 files changed, 892 insertions(+) create mode 100644 drivers/phy/artpec/Kconfig create mode 100644 drivers/phy/artpec/Makefile create mode 100644 drivers/phy/artpec/phy-artpec8-pcie.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 82b63e6..9fa4283 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -72,6 +72,7 @@ config PHY_CAN_TRANSCEIVER source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" +source "drivers/phy/artpec/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" source "drivers/phy/freescale/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 01e9eff..d748e29 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-y += allwinner/ \ amlogic/ \ + artpec/ \ broadcom/ \ diff --git a/drivers/phy/artpec/Kconfig b/drivers/phy/artpec/Kconfig new file mode 100644 index 0000000..23ae7cd --- /dev/null +++ b/drivers/phy/artpec/Kconfig @@ -0,0 +1,9 @@ +config PHY_ARTPEC8_PCIE + bool "ARTPEC-8 PCIe PHY driver" + depends on OF && (ARCH_ARTPEC || COMPILE_TEST) + select GENERIC_PHY + help + Enable PCIe PHY support for ARTPEC-8 SoC. + This driver provides PHY interface for ARTPEC-8 SoC + PCIe controller. + This is based on Samsung PCIe PHY IP. diff --git a/drivers/phy/artpec/Makefile b/drivers/phy/artpec/Makefile new file mode 100644 index 0000000..45d853c --- /dev/null +++ b/drivers/phy/artpec/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_ARTPEC8_PCIE) += phy-artpec8-pcie.o diff --git a/drivers/phy/artpec/phy-artpec8-pcie.c b/drivers/phy/artpec/phy-artpec8-pcie.c new file mode 100644 index 0000000..febbd04 --- /dev/null +++ b/drivers/phy/artpec/phy-artpec8-pcie.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PHY provider for ARTPEC-8 PCIe controller + * + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jaeho Cho <jaeho79.cho@xxxxxxxxxxx> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/debugfs.h> + +/* ARTPEC-8 PCIe PHY registers */ +/* CMN registers */ +#define PCIE_PHY_CMN_REG004 0x10 +#define PCIE_PHY_CMN_REG00B 0x2C +#define PCIE_PHY_CMN_REG016 0x58 +#define PCIE_PHY_CMN_REG01C 0x70 +#define PCIE_PHY_CMN_REG021 0x84 +#define PCIE_PHY_CMN_REG024 0x90 +#define PCIE_PHY_CMN_REG025 0x94 +#define PCIE_PHY_CMN_REG0E6 0x398 +#define PCIE_PHY_CMN_REG0E7 0x39C +#define PCIE_PHY_CMN_REG0E8 0x3A0 +#define PCIE_PHY_CMN_REG0E9 0x3A4 +#define PCIE_PHY_CMN_REG0EA 0x3A8 +#define PCIE_PHY_CMN_REG0EB 0x3AC +#define PCIE_PHY_CMN_REG0EC 0x3B0 +#define PCIE_PHY_CMN_REG0EE 0x3B8 +#define PCIE_PHY_CMN_REG0EF 0x3BC +#define PCIE_PHY_CMN_REG0F1 0x3C4 +#define PCIE_PHY_CMN_REG0F3 0x3CC +#define PCIE_PHY_CMN_REG0F4 0x3D0 + +#define PCIE_PHY_CMN_REG101 0x404 +#define OV_I_CMN_RSTN BIT(4) +#define OV_I_INIT_RSTN BIT(6) + +#define PCIE_PHY_CMN_REG131 0x4C4 +#define PCIE_PHY_CMN_REG17B 0x5EC +#define PCIE_PHY_CMN_REG17D 0x5F4 +#define PCIE_PHY_CMN_REG190 0x640 +#define PCIE_PHY_CMN_REG191 0x644 +#define PCIE_PHY_CMN_REG192 0x648 +#define PCIE_PHY_CMN_REG1C7 0x71C +#define PCIE_PHY_CMN_REG1DF 0x77C +#define PCIE_PHY_CMN_REG1E0 0x780 + +#define PCIE_PHY_CMN_REG0B1 0x2C4 +#define ANA_ROPLL_REF_DIG_CLK_SEL BIT(2) + +/* External clock */ +#define PCIE_PHY_CMN_REG14D 0x534 +#define PCIE_AUX_RX_MODE_EXTEND BIT(7) + +#define PCIE_PHY_CMN_REG0D9 0x364 +#define ANA_AUX_EXT_REF_CLK_SEL BIT(4) + +#define PCIE_PHY_CMN_REG10F 0x43C +#define AUX_PLL_EN_EXTEND BIT(4) + +#define PCIE_PHY_CMN_REG11E 0x478 +#define AUX2_PLL_EN_EXTEND BIT(3) + +#define PCIE_PHY_CMN_REG0D4 0x350 +#define OV_S_ANA_AUX_EN BIT(3) +#define OV_I_ANA_AUX_EN BIT(2) + +/* LANE registers */ +#define PCIE_PHY_TRSV_REG22D 0x8B4 +#define PCIE_PHY_TRSV_REG23E 0x8F8 +#define PCIE_PHY_TRSV_REG2A5 0xA94 +#define PCIE_PHY_TRSV_REG3E3 0xF8C +#define PCIE_PHY_TRSV_REG3ED 0xFB4 +#define PCIE_PHY_TRSV_REG20B 0x82C +#define PCIE_PHY_TRSV_REG20C 0x830 +#define PCIE_PHY_TRSV_REG234 0x8D0 +#define PCIE_PHY_TRSV_REG235 0x8D4 +#define PCIE_PHY_TRSV_REG237 0x8DC +#define PCIE_PHY_TRSV_REG239 0x8E4 +#define PCIE_PHY_TRSV_REG23A 0x8E8 +#define PCIE_PHY_TRSV_REG23B 0x8EC +#define PCIE_PHY_TRSV_REG24B 0x92C +#define PCIE_PHY_TRSV_REG25D 0x974 +#define PCIE_PHY_TRSV_REG262 0x988 +#define PCIE_PHY_TRSV_REG271 0x9C4 +#define PCIE_PHY_TRSV_REG272 0x9C8 +#define PCIE_PHY_TRSV_REG27C 0x9F0 +#define PCIE_PHY_TRSV_REG27D 0x9F4 +#define PCIE_PHY_TRSV_REG27E 0x9F8 +#define PCIE_PHY_TRSV_REG284 0xA10 +#define PCIE_PHY_TRSV_REG289 0xA24 +#define PCIE_PHY_TRSV_REG28A 0xA28 +#define PCIE_PHY_TRSV_REG28B 0xA2C +#define PCIE_PHY_TRSV_REG28C 0xA30 +#define PCIE_PHY_TRSV_REG28E 0xA38 +#define PCIE_PHY_TRSV_REG28F 0xA3C +#define PCIE_PHY_TRSV_REG290 0xA40 +#define PCIE_PHY_TRSV_REG291 0xA44 +#define PCIE_PHY_TRSV_REG292 0xA48 +#define PCIE_PHY_TRSV_REG294 0xA50 +#define PCIE_PHY_TRSV_REG295 0xA54 +#define PCIE_PHY_TRSV_REG296 0xA58 +#define PCIE_PHY_TRSV_REG297 0xA5C +#define PCIE_PHY_TRSV_REG298 0xA60 +#define PCIE_PHY_TRSV_REG29B 0xA6C +#define PCIE_PHY_TRSV_REG29C 0xA70 +#define PCIE_PHY_TRSV_REG29D 0xA74 +#define PCIE_PHY_TRSV_REG29E 0xA78 +#define PCIE_PHY_TRSV_REG2AA 0xAA8 +#define PCIE_PHY_TRSV_REG2AE 0xAB8 +#define PCIE_PHY_TRSV_REG2C2 0xB08 +#define PCIE_PHY_TRSV_REG2C6 0xB18 +#define PCIE_PHY_TRSV_REG2C7 0xB1C +#define PCIE_PHY_TRSV_REG2CB 0xB2C +#define PCIE_PHY_TRSV_REG2CC 0xB30 +#define PCIE_PHY_TRSV_REG2CD 0xB34 +#define PCIE_PHY_TRSV_REG2CE 0xB38 +#define PCIE_PHY_TRSV_REG2D0 0xB40 +#define PCIE_PHY_TRSV_REG2CF 0xB3C +#define PCIE_PHY_TRSV_REG2E0 0xB80 +#define PCIE_PHY_TRSV_REG2E9 0xBA4 +#define PCIE_PHY_TRSV_REG2EA 0xBA8 +#define PCIE_PHY_TRSV_REG2EB 0xBAC +#define PCIE_PHY_TRSV_REG315 0xC54 +#define PCIE_PHY_TRSV_REG317 0xC5C +#define PCIE_PHY_TRSV_REG319 0xC64 +#define PCIE_PHY_TRSV_REG364 0xD90 +#define PCIE_PHY_TRSV_REG36C 0xDB0 +#define PCIE_PHY_TRSV_REG36D 0xDB4 +#define PCIE_PHY_TRSV_REG37E 0xDF8 +#define PCIE_PHY_TRSV_REG37F 0xDFC +#define PCIE_PHY_TRSV_REG38F 0xE3C +#define PCIE_PHY_TRSV_REG391 0xE44 +#define PCIE_PHY_TRSV_REG39C 0xE70 +#define PCIE_PHY_TRSV_REG3A8 0xEA0 +#define PCIE_PHY_TRSV_REG3E0 0xF80 +#define PCIE_PHY_TRSV_REG3E1 0xF84 +#define PCIE_PHY_TRSV_REG3E7 0xF9C +#define PCIE_PHY_TRSV_REG3E9 0xFA4 +#define PCIE_PHY_TRSV_REG3EA 0xFA8 +#define PCIE_PHY_TRSV_REG3EE 0xFB8 +#define PCIE_PHY_TRSV_REG3EF 0xFBC +#define PCIE_PHY_TRSV_REG3F0 0xFC0 + +#define PCIE_PHY_TRSV_REG2C0 0xB00 +#define LN_EQ_CTRL_RX_DATA_HOLD BIT(5) + +/* RX Preset registers */ +#define PCIE_PHY_CMN_REG17E 0x5F8 +#define PCIE_PHY_CMN_REG180 0x600 +#define PCIE_PHY_CMN_REG181 0x604 +#define PCIE_PHY_CMN_REG182 0x608 +#define PCIE_PHY_CMN_REG183 0x60C +#define PCIE_PHY_CMN_REG184 0x610 +#define PCIE_PHY_CMN_REG185 0x614 +#define PCIE_PHY_CMN_REG186 0x618 +#define PCIE_PHY_CMN_REG187 0x61C + + +/* ARTPEC-8 PCIe PCS registers */ +#define PCIE_PCS_OUT_VEC_4 0x154 +#define B1_DYNAMIC BIT(3) + +/* ARTPEC-8 SYS REG registers */ +#define FSYS_PCIE_CON 0x424 +#define PCIE_PHY_LCPLL_REFCLK_SEL 0x3 +#define PCIE_PHY_ROPLL_REFCLK_SEL (0x3UL << 2) +#define ROPLL_REFCLK_NOT_AVAILABLE (0x2UL << 2) +#define PCIE_PHY_LN0_REFCLK_PAD_EN BIT(10) +#define PCIE_PHY_LN1_REFCLK_PAD_EN BIT(11) +#define PCIE_PHY_PWR_OFF BIT(7) + +/* ARTPEC-8 Sub Controller registers */ +#define SFR_INIT_RSTN 0x1404 +#define SFR_CMN_RSTN 0x1408 + +#define PCIE_PHY_LN0_REG_START 0x800 +#define PCIE_PHY_LN0_REG_END 0xFCC +#define OFFSET_PER_LANE 0x800 + +enum artpec8_pcie_phy_num_lanes { + LANE0 = 0, + LANE1, + LANE_MAX +}; + +struct artpec8_pcie_phy_data { + const struct phy_ops *ops; +}; + +struct artpec8_pcie_phy { + const struct artpec8_pcie_phy_data *drv_data; + void __iomem *phy_base; + void __iomem *pcs_base; + void __iomem *elbi_base; + struct clk *soc_pll_clk; + struct regmap *sysreg; + u32 lcpll_ref_clk; + const char *mode; + u32 num_lanes; +}; + +enum artpec8_pcie_ref_clk { + REF_CLK_FROM_XO = 0, + REF_CLK_FROM_IO, + REF_CLK_RESERVED, + REF_CLK_FROM_SOC_PLL, + REF_CLK_MAX +}; + +struct artpec8_pcie_phy_tune_reg { + u32 offset; + u32 val; +}; + +/* ARTPEC-8 PCIe Gen4 x2 PHY CMN register settings */ +struct artpec8_pcie_phy_tune_reg cmn_regs[] = { + {PCIE_PHY_CMN_REG004, 0x65}, + {PCIE_PHY_CMN_REG00B, 0x18}, + {PCIE_PHY_CMN_REG016, 0x0E}, + {PCIE_PHY_CMN_REG01C, 0x4F}, + {PCIE_PHY_CMN_REG021, 0x01}, + {PCIE_PHY_CMN_REG024, 0x58}, + {PCIE_PHY_CMN_REG025, 0x98}, + {PCIE_PHY_CMN_REG0E6, 0x00}, + {PCIE_PHY_CMN_REG0E7, 0x00}, + {PCIE_PHY_CMN_REG0E8, 0x3F}, + {PCIE_PHY_CMN_REG0E9, 0x3F}, + {PCIE_PHY_CMN_REG0EA, 0xFF}, + {PCIE_PHY_CMN_REG0EB, 0xFF}, + {PCIE_PHY_CMN_REG0EC, 0x42}, + {PCIE_PHY_CMN_REG0EE, 0x3F}, + {PCIE_PHY_CMN_REG0EF, 0x7F}, + {PCIE_PHY_CMN_REG0F1, 0x02}, + {PCIE_PHY_CMN_REG0F3, 0xFF}, + {PCIE_PHY_CMN_REG0F4, 0xFF}, + {PCIE_PHY_CMN_REG131, 0x01}, + {PCIE_PHY_CMN_REG17B, 0xC0}, + {PCIE_PHY_CMN_REG17D, 0xAF}, + {PCIE_PHY_CMN_REG190, 0x27}, + {PCIE_PHY_CMN_REG191, 0x0F}, + {PCIE_PHY_CMN_REG192, 0x3F}, + {PCIE_PHY_CMN_REG1C7, 0x05}, + {PCIE_PHY_CMN_REG1DF, 0x28}, + {PCIE_PHY_CMN_REG1E0, 0x28}, +}; + +/* ARTPEC-8 PCIe Gen4 x2 PHY lane register settings */ +struct artpec8_pcie_phy_tune_reg lane_regs[] = { + {PCIE_PHY_TRSV_REG22D, 0x00}, + {PCIE_PHY_TRSV_REG23E, 0x00}, + {PCIE_PHY_TRSV_REG2A5, 0x73}, + {PCIE_PHY_TRSV_REG3E3, 0x7B}, + {PCIE_PHY_TRSV_REG3ED, 0x4B}, + {PCIE_PHY_TRSV_REG20B, 0x02}, + {PCIE_PHY_TRSV_REG20C, 0xEA}, + {PCIE_PHY_TRSV_REG234, 0x7A}, + {PCIE_PHY_TRSV_REG235, 0x1C}, + {PCIE_PHY_TRSV_REG237, 0x10}, + {PCIE_PHY_TRSV_REG239, 0x68}, + {PCIE_PHY_TRSV_REG23A, 0xC0}, + {PCIE_PHY_TRSV_REG23B, 0x0B}, + {PCIE_PHY_TRSV_REG24B, 0x00}, + {PCIE_PHY_TRSV_REG25D, 0x07}, + {PCIE_PHY_TRSV_REG262, 0x07}, + {PCIE_PHY_TRSV_REG271, 0x23}, + {PCIE_PHY_TRSV_REG272, 0x5E}, + {PCIE_PHY_TRSV_REG27C, 0x8C}, + {PCIE_PHY_TRSV_REG27D, 0x5B}, + {PCIE_PHY_TRSV_REG27E, 0x2C}, + {PCIE_PHY_TRSV_REG284, 0x33}, + {PCIE_PHY_TRSV_REG289, 0xD4}, + {PCIE_PHY_TRSV_REG28A, 0xCC}, + {PCIE_PHY_TRSV_REG28B, 0xD9}, + {PCIE_PHY_TRSV_REG28C, 0xDC}, + {PCIE_PHY_TRSV_REG28E, 0xC6}, + {PCIE_PHY_TRSV_REG28F, 0x90}, + {PCIE_PHY_TRSV_REG290, 0x4D}, + {PCIE_PHY_TRSV_REG291, 0x19}, + {PCIE_PHY_TRSV_REG292, 0x1C}, + {PCIE_PHY_TRSV_REG294, 0x05}, + {PCIE_PHY_TRSV_REG295, 0x10}, + {PCIE_PHY_TRSV_REG296, 0x0C}, + {PCIE_PHY_TRSV_REG297, 0x19}, + {PCIE_PHY_TRSV_REG298, 0x04}, + {PCIE_PHY_TRSV_REG29B, 0x03}, + {PCIE_PHY_TRSV_REG29C, 0x1B}, + {PCIE_PHY_TRSV_REG29D, 0x1B}, + {PCIE_PHY_TRSV_REG29E, 0x1F}, + {PCIE_PHY_TRSV_REG2AA, 0x00}, + {PCIE_PHY_TRSV_REG2AE, 0x1F}, + {PCIE_PHY_TRSV_REG2C2, 0x25}, + {PCIE_PHY_TRSV_REG2C6, 0x10}, + {PCIE_PHY_TRSV_REG2C7, 0x06}, + {PCIE_PHY_TRSV_REG2CB, 0x10}, + {PCIE_PHY_TRSV_REG2CC, 0x06}, + {PCIE_PHY_TRSV_REG2CD, 0x20}, + {PCIE_PHY_TRSV_REG2CE, 0x27}, + {PCIE_PHY_TRSV_REG2D0, 0x10}, + {PCIE_PHY_TRSV_REG2CF, 0x0A}, + {PCIE_PHY_TRSV_REG2E0, 0x01}, + {PCIE_PHY_TRSV_REG2E9, 0x11}, + {PCIE_PHY_TRSV_REG2EA, 0x05}, + {PCIE_PHY_TRSV_REG2EB, 0x4C}, + {PCIE_PHY_TRSV_REG315, 0x18}, + {PCIE_PHY_TRSV_REG317, 0x86}, + {PCIE_PHY_TRSV_REG319, 0x8E}, + {PCIE_PHY_TRSV_REG364, 0x00}, + {PCIE_PHY_TRSV_REG36C, 0x03}, + {PCIE_PHY_TRSV_REG36D, 0x04}, + {PCIE_PHY_TRSV_REG37E, 0x06}, + {PCIE_PHY_TRSV_REG37F, 0x04}, + {PCIE_PHY_TRSV_REG38F, 0x40}, + {PCIE_PHY_TRSV_REG391, 0x8B}, + {PCIE_PHY_TRSV_REG39C, 0xFF}, + {PCIE_PHY_TRSV_REG3A8, 0x02}, + {PCIE_PHY_TRSV_REG3E0, 0x93}, + {PCIE_PHY_TRSV_REG3E1, 0x79}, + {PCIE_PHY_TRSV_REG3E7, 0xF5}, + {PCIE_PHY_TRSV_REG3E9, 0x75}, + {PCIE_PHY_TRSV_REG3EA, 0x0D}, + {PCIE_PHY_TRSV_REG3EE, 0xE2}, + {PCIE_PHY_TRSV_REG3EF, 0x6F}, + {PCIE_PHY_TRSV_REG3F0, 0x3D} +}; +#define PCIE_PHY_CMN_REG17E 0x5F8 +#define PCIE_PHY_CMN_REG180 0x600 +#define PCIE_PHY_CMN_REG181 0x604 +#define PCIE_PHY_CMN_REG182 0x608 +#define PCIE_PHY_CMN_REG183 0x60C +#define PCIE_PHY_CMN_REG184 0x610 +#define PCIE_PHY_CMN_REG185 0x614 +#define PCIE_PHY_CMN_REG186 0x618 +#define PCIE_PHY_CMN_REG187 0x61C + +struct artpec8_pcie_phy_tune_reg rx_preset_regs[] = { + /* 0 */ + {PCIE_PHY_CMN_REG17E, 0x00}, + {PCIE_PHY_CMN_REG180, 0x23}, + {PCIE_PHY_CMN_REG181, 0x44}, + {PCIE_PHY_CMN_REG182, 0x61}, + {PCIE_PHY_CMN_REG183, 0x55}, + {PCIE_PHY_CMN_REG184, 0x14}, + {PCIE_PHY_CMN_REG185, 0x23}, + {PCIE_PHY_CMN_REG186, 0x1A}, + {PCIE_PHY_CMN_REG187, 0x04}, + {PCIE_PHY_CMN_REG17E, 0x04}, + {PCIE_PHY_CMN_REG17E, 0x00}, + /* 1 */ + {PCIE_PHY_CMN_REG17E, 0x08}, + {PCIE_PHY_CMN_REG181, 0x42}, + {PCIE_PHY_CMN_REG17E, 0x0C}, + {PCIE_PHY_CMN_REG17E, 0x08}, + /* 2 */ + {PCIE_PHY_CMN_REG17E, 0x10}, + {PCIE_PHY_CMN_REG181, 0x40}, + {PCIE_PHY_CMN_REG17E, 0x14}, + {PCIE_PHY_CMN_REG17E, 0x10}, + /* 3 */ + {PCIE_PHY_CMN_REG17E, 0x18}, + {PCIE_PHY_CMN_REG181, 0x45}, + {PCIE_PHY_CMN_REG17E, 0x1C}, + {PCIE_PHY_CMN_REG17E, 0x18}, + /* 4 */ + {PCIE_PHY_CMN_REG17E, 0x20}, + {PCIE_PHY_CMN_REG181, 0x46}, + {PCIE_PHY_CMN_REG17E, 0x24}, + {PCIE_PHY_CMN_REG17E, 0x20}, + /* 5 */ + {PCIE_PHY_CMN_REG17E, 0x28}, + {PCIE_PHY_CMN_REG181, 0x48}, + {PCIE_PHY_CMN_REG17E, 0x2C}, + {PCIE_PHY_CMN_REG17E, 0x28}, + /* 6 */ + {PCIE_PHY_CMN_REG17E, 0x30}, + {PCIE_PHY_CMN_REG181, 0x4A}, + {PCIE_PHY_CMN_REG17E, 0x34}, + {PCIE_PHY_CMN_REG17E, 0x30}, + /* 7 */ + {PCIE_PHY_CMN_REG17E, 0x38}, + {PCIE_PHY_CMN_REG181, 0x4C}, + {PCIE_PHY_CMN_REG17E, 0x3C}, + {PCIE_PHY_CMN_REG17E, 0x38}, + /* 8 */ + {PCIE_PHY_CMN_REG17E, 0x40}, + {PCIE_PHY_CMN_REG180, 0x20}, + {PCIE_PHY_CMN_REG181, 0x20}, + {PCIE_PHY_CMN_REG182, 0x01}, + {PCIE_PHY_CMN_REG17E, 0x44}, + {PCIE_PHY_CMN_REG17E, 0x40}, + /* 9 */ + {PCIE_PHY_CMN_REG17E, 0x48}, + {PCIE_PHY_CMN_REG180, 0x20}, + {PCIE_PHY_CMN_REG181, 0x21}, + {PCIE_PHY_CMN_REG182, 0x01}, + {PCIE_PHY_CMN_REG17E, 0x4C}, + {PCIE_PHY_CMN_REG17E, 0x48}, + /* 10 */ + {PCIE_PHY_CMN_REG17E, 0x50}, + {PCIE_PHY_CMN_REG180, 0x24}, + {PCIE_PHY_CMN_REG181, 0x80}, + {PCIE_PHY_CMN_REG182, 0x41}, + {PCIE_PHY_CMN_REG183, 0xAF}, + {PCIE_PHY_CMN_REG184, 0x26}, + {PCIE_PHY_CMN_REG185, 0x34}, + {PCIE_PHY_CMN_REG186, 0x24}, + {PCIE_PHY_CMN_REG187, 0x06}, + {PCIE_PHY_CMN_REG17E, 0x54}, + {PCIE_PHY_CMN_REG17E, 0x50}, + /* 11 */ + {PCIE_PHY_CMN_REG17E, 0x58}, + {PCIE_PHY_CMN_REG181, 0x81}, + {PCIE_PHY_CMN_REG17E, 0x5C}, + {PCIE_PHY_CMN_REG17E, 0x58}, + /* 12 */ + {PCIE_PHY_CMN_REG17E, 0x60}, + {PCIE_PHY_CMN_REG181, 0x82}, + {PCIE_PHY_CMN_REG17E, 0x64}, + {PCIE_PHY_CMN_REG17E, 0x60}, + /* 13 */ + {PCIE_PHY_CMN_REG17E, 0x68}, + {PCIE_PHY_CMN_REG181, 0x83}, + {PCIE_PHY_CMN_REG17E, 0x6C}, + {PCIE_PHY_CMN_REG17E, 0x68}, + /* 14 */ + {PCIE_PHY_CMN_REG17E, 0x70}, + {PCIE_PHY_CMN_REG181, 0x84}, + {PCIE_PHY_CMN_REG17E, 0x74}, + {PCIE_PHY_CMN_REG17E, 0x70}, + /* 15 */ + {PCIE_PHY_CMN_REG17E, 0x78}, + {PCIE_PHY_CMN_REG180, 0x24}, + {PCIE_PHY_CMN_REG181, 0x85}, + {PCIE_PHY_CMN_REG182, 0x80}, + {PCIE_PHY_CMN_REG183, 0x7F}, + {PCIE_PHY_CMN_REG184, 0x2D}, + {PCIE_PHY_CMN_REG185, 0x34}, + {PCIE_PHY_CMN_REG186, 0x24}, + {PCIE_PHY_CMN_REG187, 0x05}, + {PCIE_PHY_CMN_REG17E, 0x7C}, + {PCIE_PHY_CMN_REG17E, 0x78}, + /* 16 */ + {PCIE_PHY_CMN_REG17E, 0x80}, + {PCIE_PHY_CMN_REG181, 0x86}, + {PCIE_PHY_CMN_REG17E, 0x84}, + {PCIE_PHY_CMN_REG17E, 0x80}, + /* 17 */ + {PCIE_PHY_CMN_REG17E, 0x88}, + {PCIE_PHY_CMN_REG181, 0x87}, + {PCIE_PHY_CMN_REG17E, 0x8C}, + {PCIE_PHY_CMN_REG17E, 0x88}, + /* 18 */ + {PCIE_PHY_CMN_REG17E, 0x90}, + {PCIE_PHY_CMN_REG181, 0x88}, + {PCIE_PHY_CMN_REG17E, 0x94}, + {PCIE_PHY_CMN_REG17E, 0x90}, + /* 19 */ + {PCIE_PHY_CMN_REG17E, 0x98}, + {PCIE_PHY_CMN_REG181, 0x89}, + {PCIE_PHY_CMN_REG17E, 0x9C}, + {PCIE_PHY_CMN_REG17E, 0x98}, +}; + + +static void artpec8_pcie_phy_reg_writel(void __iomem *base, u32 val, u32 reg) +{ + writel(val, base + reg); +}; + +static u32 artpec8_pcie_phy_reg_readl(void __iomem *base, u32 reg) +{ + return readl(base + reg); +}; + +static struct dentry *debugfs_dir; + +/* FOM registers */ +#define PCIE_PHY_FOM_7_0 0xD3C /* TRSV_REG34F */ +#define PCIE_PHY_FOM_15_8 0xF30 /* Undocumented */ +#define PCIE_PHY_FOM_23_16 0xF34 /* Undocumented */ +#define PCIE_PHY_FOM_31_24 0xF2C /* Undocumented */ + +static int fom_read(void *data, u64 *value) +{ + struct phy *phy = data; + struct artpec8_pcie_phy *artpec8_phy = phy_get_drvdata(phy); + uint32_t fom; + + fom = artpec8_pcie_phy_reg_readl(artpec8_phy->phy_base, + PCIE_PHY_FOM_7_0) & 0xff; + fom |= (artpec8_pcie_phy_reg_readl(artpec8_phy->phy_base, + PCIE_PHY_FOM_15_8) & 0xff) << 8; + fom |= (artpec8_pcie_phy_reg_readl(artpec8_phy->phy_base, + PCIE_PHY_FOM_23_16) & 0xff) << 16; + fom |= (artpec8_pcie_phy_reg_readl(artpec8_phy->phy_base, + PCIE_PHY_FOM_31_24) & 0xff) << 24; + *value = fom; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fom_fops, fom_read, NULL, "%llu\n"); + + +static void artpec8_pcie_phy_reg_update(void __iomem *base, u32 mask, + u32 update, u32 reg) +{ + u32 val; + + val = artpec8_pcie_phy_reg_readl(base, reg); + val &= ~(mask); + val |= update; + artpec8_pcie_phy_reg_writel(base, val, reg); +}; + +static void artpec8_pcie_enable_ref_clk_from_xo(struct artpec8_pcie_phy + *pciephy) +{ + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_LCPLL_REFCLK_SEL, + REF_CLK_FROM_XO); + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_ROPLL_REFCLK_SEL, + ROPLL_REFCLK_NOT_AVAILABLE); +}; + +static void artpec8_pcie_enable_ref_clk_from_io(struct artpec8_pcie_phy + *pciephy) +{ + artpec8_pcie_phy_reg_update(pciephy->phy_base, + PCIE_AUX_RX_MODE_EXTEND, 0, + PCIE_PHY_CMN_REG14D); + artpec8_pcie_phy_reg_update(pciephy->phy_base, + ANA_AUX_EXT_REF_CLK_SEL, 0, + PCIE_PHY_CMN_REG0D9); + artpec8_pcie_phy_reg_update(pciephy->phy_base, + AUX_PLL_EN_EXTEND, 0, + PCIE_PHY_CMN_REG10F); + artpec8_pcie_phy_reg_update(pciephy->phy_base, + AUX2_PLL_EN_EXTEND, 0, + PCIE_PHY_CMN_REG11E); + artpec8_pcie_phy_reg_update(pciephy->phy_base, + OV_S_ANA_AUX_EN, OV_S_ANA_AUX_EN, + PCIE_PHY_CMN_REG0D4); + artpec8_pcie_phy_reg_update(pciephy->phy_base, + OV_I_ANA_AUX_EN, OV_I_ANA_AUX_EN, + PCIE_PHY_CMN_REG0D4); + + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_LCPLL_REFCLK_SEL, + REF_CLK_FROM_IO); + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_ROPLL_REFCLK_SEL, + ROPLL_REFCLK_NOT_AVAILABLE); + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_LN0_REFCLK_PAD_EN, + PCIE_PHY_LN0_REFCLK_PAD_EN); + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_LN1_REFCLK_PAD_EN, + PCIE_PHY_LN1_REFCLK_PAD_EN); +} + +static void artpec8_pcie_enable_ref_clk_from_soc_pll(struct artpec8_pcie_phy + *pciephy) +{ + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_LCPLL_REFCLK_SEL, + REF_CLK_FROM_SOC_PLL); + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_ROPLL_REFCLK_SEL, + ROPLL_REFCLK_NOT_AVAILABLE); +} + +static void artpec8_pcie_lane_control(struct phy *phy, u32 lane0_reg, u32 mask, + u32 val) +{ + struct artpec8_pcie_phy *pciephy = phy_get_drvdata(phy); + u32 lanex_reg = lane0_reg; + int i; + + if (lane0_reg < PCIE_PHY_LN0_REG_START || + lane0_reg > PCIE_PHY_LN0_REG_END) { + dev_err(&phy->dev, "offset [0x%04x] out of range of lane0\n", + lane0_reg); + return; + } + + + for (i = 0; i < pciephy->num_lanes; i++) { + lanex_reg += OFFSET_PER_LANE * i; + artpec8_pcie_phy_reg_update(pciephy->phy_base, mask, val, + lanex_reg); + } +} + +static void artpec8_pcie_phy_tune(struct artpec8_pcie_phy *pciephy) +{ + int i, count; + + /* init cmn registers */ + count = ARRAY_SIZE(cmn_regs); + for (i = 0; i < count; i++) { + artpec8_pcie_phy_reg_writel(pciephy->phy_base, + cmn_regs[i].val, + cmn_regs[i].offset); + } + + /* init lane registers */ + count = ARRAY_SIZE(lane_regs); + for (i = 0; i < count; i++) { + artpec8_pcie_phy_reg_writel(pciephy->phy_base, + lane_regs[i].val, + lane_regs[i].offset); + artpec8_pcie_phy_reg_writel(pciephy->phy_base, + lane_regs[i].val, + lane_regs[i].offset + + OFFSET_PER_LANE); + } + + /* rx preset registers */ + count = ARRAY_SIZE(rx_preset_regs); + for (i = 0; i < count; i++) { + artpec8_pcie_phy_reg_writel(pciephy->phy_base, + rx_preset_regs[i].val, + rx_preset_regs[i].offset); + } +} + +static int artpec8_pcie_phy_init(struct phy *phy) +{ + struct artpec8_pcie_phy *pciephy = phy_get_drvdata(phy); + + /* reset init_rstn and cmn_rstn */ + artpec8_pcie_phy_reg_update(pciephy->phy_base, OV_I_CMN_RSTN | OV_I_INIT_RSTN, + 0, PCIE_PHY_CMN_REG101); + + /* reference clock selection */ + switch (pciephy->lcpll_ref_clk) { + case REF_CLK_FROM_XO: + artpec8_pcie_enable_ref_clk_from_xo(pciephy); + break; + case REF_CLK_FROM_IO: + artpec8_pcie_enable_ref_clk_from_io(pciephy); + break; + case REF_CLK_FROM_SOC_PLL: + artpec8_pcie_enable_ref_clk_from_soc_pll(pciephy); + break; + default: + dev_err(&phy->dev, "invalid reference clock [%d]\n", + pciephy->lcpll_ref_clk); + break; + } + + dev_dbg(&phy->dev, "phy init, lcpll_ref_clk[%d]\n", + pciephy->lcpll_ref_clk); + + /* release i_init_rstn */ + artpec8_pcie_phy_reg_update(pciephy->phy_base, OV_I_INIT_RSTN, + OV_I_INIT_RSTN, PCIE_PHY_CMN_REG101); + + /* phy initial settings */ + artpec8_pcie_phy_tune(pciephy); + + /* pll_en should be set to off when PM_STATE is P1.CPM */ + if (!strncmp(pciephy->mode, "pcie_ep", strlen("pcie_ep"))) { + artpec8_pcie_phy_reg_update(pciephy->pcs_base, B1_DYNAMIC, + B1_DYNAMIC, PCIE_PCS_OUT_VEC_4); + } + + /* disable lane eq ctrl rx data hold */ + artpec8_pcie_lane_control(phy, PCIE_PHY_TRSV_REG2C0, + LN_EQ_CTRL_RX_DATA_HOLD, 0); + + return 0; +} + +static int artpec8_pcie_phy_exit(struct phy *phy) +{ + dev_dbg(&phy->dev, "phy exit\n"); + + return 0; +} + +static int artpec8_pcie_phy_reset(struct phy *phy) +{ + struct artpec8_pcie_phy *pciephy = phy_get_drvdata(phy); + + artpec8_pcie_phy_reg_update(pciephy->phy_base, OV_I_CMN_RSTN, + 0, PCIE_PHY_CMN_REG101); + udelay(10); + artpec8_pcie_phy_reg_update(pciephy->phy_base, OV_I_CMN_RSTN, + OV_I_CMN_RSTN, PCIE_PHY_CMN_REG101); + + dev_dbg(&phy->dev, "phy reset\n"); + + return 0; +} + +static int artpec8_pcie_phy_power_on(struct phy *phy) +{ + struct artpec8_pcie_phy *pciephy = phy_get_drvdata(phy); + + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_PWR_OFF, 0); + + dev_dbg(&phy->dev, "phy power on\n"); + + return 0; +} + +static int artpec8_pcie_phy_power_off(struct phy *phy) +{ + struct artpec8_pcie_phy *pciephy = phy_get_drvdata(phy); + + regmap_update_bits(pciephy->sysreg, FSYS_PCIE_CON, + PCIE_PHY_PWR_OFF, PCIE_PHY_PWR_OFF); + + dev_dbg(&phy->dev, "phy power off\n"); + + return 0; +} + +static const struct phy_ops artpec8_phy_ops = { + .init = artpec8_pcie_phy_init, + .exit = artpec8_pcie_phy_exit, + .reset = artpec8_pcie_phy_reset, + .power_on = artpec8_pcie_phy_power_on, + .power_off = artpec8_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct artpec8_pcie_phy_data artpec8_pcie_phy_data = { + .ops = &artpec8_phy_ops, +}; + +static const struct of_device_id artpec8_pcie_phy_match[] = { + { + .compatible = "axis,artpec8-pcie-phy", + .data = &artpec8_pcie_phy_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, artpec8_pcie_phy_match); + +static int artpec8_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct artpec8_pcie_phy *artpec8_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct resource *res; + const struct artpec8_pcie_phy_data *drv_data; + + drv_data = of_device_get_match_data(dev); + if (!drv_data) + return -ENODEV; + + artpec8_phy = devm_kzalloc(dev, sizeof(*artpec8_phy), GFP_KERNEL); + if (!artpec8_phy) + return -ENOMEM; + + /* reference clock */ + if (of_property_read_u32(dev->of_node, "lcpll-ref-clk", + &artpec8_phy->lcpll_ref_clk)) { + dev_err(dev, "Failed selecting the lcpll-ref-clk\n"); + return -EINVAL; + } + /* PLL SOC reference clock */ + if (artpec8_phy->lcpll_ref_clk == REF_CLK_FROM_SOC_PLL) { + artpec8_phy->soc_pll_clk = devm_clk_get(dev, "ref_clk"); + if (IS_ERR(artpec8_phy->soc_pll_clk)) { + dev_err(dev, "couldn't get reference clock\n"); + return -EINVAL; + } + clk_prepare_enable(artpec8_phy->soc_pll_clk); + } + + /* link mode */ + if (of_property_read_string(dev->of_node, "mode", &artpec8_phy->mode)) { + dev_err(dev, "failed to get mode\n"); + return -EINVAL; + } + + /* number of lanes */ + if (of_property_read_u32(dev->of_node, "num-lanes", + &artpec8_phy->num_lanes)) { + dev_err(dev, "failed to get num-lanes\n"); + return -EINVAL; + } + + if (artpec8_phy->num_lanes > LANE_MAX) { + dev_err(dev, "invalid num_lanes[%u]\n", artpec8_phy->num_lanes); + return -EINVAL; + } + + /* PHY base register */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + artpec8_phy->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(artpec8_phy->phy_base)) + return PTR_ERR(artpec8_phy->phy_base); + + /* PCS base register */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcs"); + artpec8_phy->pcs_base = devm_ioremap_resource(dev, res); + if (IS_ERR(artpec8_phy->pcs_base)) + return PTR_ERR(artpec8_phy->pcs_base); + + /* sysreg regmap handle, need to change using smc */ + artpec8_phy->sysreg = + syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,fsys-sysreg"); + if (IS_ERR(artpec8_phy->sysreg)) { + dev_err(dev, "fsys sysreg regmap lookup failed.\n"); + return PTR_ERR(artpec8_phy->sysreg); + } + + artpec8_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, artpec8_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(phy_provider); + } + + /* Add FOM in debugfs */ + debugfs_dir = debugfs_create_dir("pcie-phy", NULL); + debugfs_create_file("phy-fom", 0444, debugfs_dir, + generic_phy, &fom_fops); + + dev_info(dev, "registered PCIe PHY\n"); + + return 0; +} + +static int __exit artpec8_pcie_remove(struct platform_device *pdev) +{ + struct artpec8_pcie_phy *artpec8_phy = platform_get_drvdata(pdev); + + /* Remove debugfs files for FOM values */ + debugfs_remove_recursive(debugfs_dir); + + if (artpec8_phy->soc_pll_clk) + clk_disable_unprepare(artpec8_phy->soc_pll_clk); + + return 0; +} + +static struct platform_driver artpec8_pcie_phy_driver = { + .probe = artpec8_pcie_phy_probe, + .remove = __exit_p(artpec8_pcie_phy_remove), + .driver = { + .of_match_table = artpec8_pcie_phy_match, + .name = "artpec8_pcie_phy", + } +}; + +module_platform_driver(artpec8_pcie_phy_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaeho Cho <jaeho79.cho@xxxxxxxxxxx>"); -- 2.9.5